TextKit in Xamarin.iOS
TextKit ist eine neue API, die leistungsstarke Textlayout- und Renderingfunktionen bietet. Es basiert auf dem Core-Text-Framework auf niedriger Ebene, ist aber viel einfacher zu verwenden als Core Text.
Um die Features von TextKit für Standardsteuerelemente verfügbar zu machen, wurden mehrere iOS-Textsteuerelemente für die Verwendung von TextKit neu implementiert, darunter:
- UITextView
- UITextField
- UILabel
Aufbau
TextKit bietet eine mehrschichtige Architektur, die den Textspeicher vom Layout und der Anzeige trennt, einschließlich der folgenden Klassen:
NSTextContainer
– Stellt das Koordinatensystem und die Geometrie bereit, das zum Layouttext verwendet wird.NSLayoutManager
– Gliedert Text, indem Text in Glyphen umgewandelt wird.NSTextStorage
– Enthält die Textdaten sowie behandelt Batch-Texteigenschaftenaktualisierungen. Alle Batchaktualisierungen werden dem Layout-Manager zur tatsächlichen Verarbeitung der Änderungen übergeben, z. B. das Neuberechnen des Layouts und das Neurapen des Texts.
Diese drei Klassen werden auf eine Ansicht angewendet, die Text rendert. Die integrierten Textverarbeitungsansichten, zUITextView
UITextField
. B. , und UILabel
sie sind bereits festgelegt, sie können aber auch auf jede UIView
Instanz erstellt und angewendet werden.
Die folgende Abbildung veranschaulicht diese Architektur:
Textspeicher und Attribute
Die NSTextStorage
Klasse enthält den Text, der von einer Ansicht angezeigt wird. Außerdem werden änderungen an dem Text , z. B. Änderungen an Zeichen oder deren Attributen, an den Layout-Manager für die Anzeige übermittelt. NSTextStorage
erbt von einer MSMutableAttributed
Zeichenfolge, sodass Änderungen an Textattributen in Batches zwischen BeginEditing
und EndEditing
Aufrufen angegeben werden können.
Der folgende Codeausschnitt gibt beispielsweise eine Änderung der Vordergrund- bzw. Hintergrundfarben an und zielt auf bestimmte Bereiche ab:
textView.TextStorage.BeginEditing ();
textView.TextStorage.AddAttribute(UIStringAttributeKey.ForegroundColor, UIColor.Green, new NSRange(200, 400));
textView.TextStorage.AddAttribute(UIStringAttributeKey.BackgroundColor, UIColor.Black, new NSRange(210, 300));
textView.TextStorage.EndEditing ();
Nach EndEditing
dem Aufruf werden die Änderungen an den Layout-Manager gesendet, wodurch wiederum alle erforderlichen Layout- und Renderingberechnungen ausgeführt werden, damit der Text in der Ansicht angezeigt wird.
Layout mit Ausschlusspfad
TextKit unterstützt auch das Layout und ermöglicht komplexe Szenarien wie mehrspaltigen Text und fließenden Text um angegebene Pfade, die als Ausschlusspfade bezeichnet werden. Ausschlusspfade werden auf den Textcontainer angewendet, der die Geometrie des Textlayouts ändert, wodurch der Text um die angegebenen Pfade fließt.
Zum Hinzufügen eines Ausschlusspfads muss die ExclusionPaths
Eigenschaft im Layout-Manager festgelegt werden. Das Festlegen dieser Eigenschaft bewirkt, dass der Layout-Manager das Textlayout ungültig macht und den Text um den Ausschlusspfad fließt.
Ausschluss basierend auf einem CGPath
Berücksichtigen Sie die folgende UITextView
Unterklassenimplementierung:
public class ExclusionPathView : UITextView
{
CGPath exclusionPath;
CGPoint initialPoint;
CGPoint latestPoint;
UIBezierPath bezierPath;
public ExclusionPathView (string text)
{
Text = text;
ContentInset = new UIEdgeInsets (20, 0, 0, 0);
BackgroundColor = UIColor.White;
exclusionPath = new CGPath ();
bezierPath = UIBezierPath.Create ();
LayoutManager.AllowsNonContiguousLayout = false;
}
public override void TouchesBegan (NSSet touches, UIEvent evt)
{
base.TouchesBegan (touches, evt);
var touch = touches.AnyObject as UITouch;
if (touch != null) {
initialPoint = touch.LocationInView (this);
}
}
public override void TouchesMoved (NSSet touches, UIEvent evt)
{
base.TouchesMoved (touches, evt);
UITouch touch = touches.AnyObject as UITouch;
if (touch != null) {
latestPoint = touch.LocationInView (this);
SetNeedsDisplay ();
}
}
public override void TouchesEnded (NSSet touches, UIEvent evt)
{
base.TouchesEnded (touches, evt);
bezierPath.CGPath = exclusionPath;
TextContainer.ExclusionPaths = new UIBezierPath[] { bezierPath };
}
public override void Draw (CGRect rect)
{
base.Draw (rect);
if (!initialPoint.IsEmpty) {
using (var g = UIGraphics.GetCurrentContext ()) {
g.SetLineWidth (4);
UIColor.Blue.SetStroke ();
if (exclusionPath.IsEmpty) {
exclusionPath.AddLines (new CGPoint[] { initialPoint, latestPoint });
} else {
exclusionPath.AddLineToPoint (latestPoint);
}
g.AddPath (exclusionPath);
g.DrawPath (CGPathDrawingMode.Stroke);
}
}
}
}
Dieser Code fügt Unterstützung für das Zeichnen in der Textansicht mithilfe von Core Graphics hinzu. Da die Klasse jetzt für die UITextView
Verwendung von TextKit für das Textrendering und -layout erstellt wurde, kann sie alle Features von TextKit verwenden, z. B. das Festlegen von Ausschlusspfaden.
Wichtig
In diesem Beispiel werden Unterklassen zum Hinzufügen von Touchzeichnungsunterstützung verwendet UITextView
. Unterklassen UITextView
sind nicht erforderlich, um die Features von TextKit abzurufen.
Nachdem der Benutzer die Textansicht gezeichnet hat, wird das Zeichnen CGPath
auf eine UIBezierPath
Instanz angewendet, indem die UIBezierPath.CGPath
Eigenschaft festgelegt wird:
bezierPath.CGPath = exclusionPath;
Durch das Aktualisieren der folgenden Codezeile wird das Textlayout um den Pfad aktualisiert:
TextContainer.ExclusionPaths = new UIBezierPath[] { bezierPath };
Der folgende Screenshot veranschaulicht, wie sich das Textlayout um den gezeichneten Pfad ändert:
Beachten Sie, dass die Eigenschaft des AllowsNonContiguousLayout
Layout-Managers in diesem Fall auf "false" festgelegt ist. Dadurch wird das Layout für alle Fälle neu berechnet, in denen sich der Text ändert. Wenn Sie dies auf "true" festlegen, kann dies von der Leistung profitieren, indem Sie eine Volllayoutaktualisierung vermeiden, insbesondere bei großen Dokumenten. Die Einstellung AllowsNonContiguousLayout
auf "true" würde jedoch verhindern, dass der Ausschlusspfad das Layout unter bestimmten Umständen aktualisiert, z. B. wenn Text zur Laufzeit ohne nachfolgende Wagenrücklauf vor dem Festlegen des Pfads eingegeben wird.