TextKit in Xamarin.iOS
TextKit ist eine neue API, die leistungsstarke Textlayout- und Renderingfunktionen bietet. Es basiert auf dem Low-Level Core Text-Framework, 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, die zum Layout von Text verwendet werden.NSLayoutManager
– Legt Text aus, indem Text in Glyphen umgewandelt wird.NSTextStorage
– Enthält die Textdaten und verarbeitet Batchtexteigenschaftenupdates. Alle Batchaktualisierungen werden an den Layout-Manager für die tatsächliche Verarbeitung der Änderungen übergeben, z. B. das Neu berechnen des Layouts und das Neu zeichnen des Texts.
Diese drei Klassen werden auf eine Ansicht angewendet, die Text rendert. Die integrierten Textverarbeitungsansichten wie UITextView
, UITextField
und UILabel
sind bereits festgelegt, aber Sie können sie auch erstellen und auf alle UIView
instance anwenden.
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 alle Änderungen am Text - z. B. Änderungen an Zeichen oder deren Attributen - an den Layout-Manager zur Anzeige übermittelt. NSTextStorage
Erbt von 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 dem EndEditing
Aufruf werden die Änderungen an den Layout-Manager gesendet, der wiederum alle erforderlichen Layout- und Renderingberechnungen für den Text ausführt, der in der Ansicht angezeigt werden soll.
Layout mit Ausschlusspfad
TextKit unterstützt auch 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, sodass der Text um die angegebenen Pfade fließt.
Das Hinzufügen eines Ausschlusspfads erfordert das Festlegen der ExclusionPaths
Eigenschaft im Layout-Manager. Das Festlegen dieser Eigenschaft bewirkt, dass der Layout-Manager das Textlayout ungültig macht und den Text um den Ausschlusspfad herumfließt.
Ausschluss basierend auf einem CGPath
Betrachten 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 mit Core Graphics hinzu. Da die UITextView
-Klasse jetzt so erstellt ist, dass textKit für ihr Textrendering und -layout verwendet wird, kann sie alle Funktionen von TextKit verwenden, z. B. das Festlegen von Ausschlusspfaden.
Wichtig
In diesem Beispiel werden Unterklassen zum Hinzufügen von Touchzeichnungsunterstützung erstellt UITextView
. Unterklassen UITextView
sind nicht erforderlich, um die Funktionen von TextKit zu erhalten.
Nachdem der Benutzer die Textansicht gezeichnet hat, wird das Gezeichnete CGPath
auf eine UIBezierPath
instance 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 ändert, um den gezeichneten Pfad zu durchlaufen:
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 der Leistung zugute kommen, da eine vollständige Layoutaktualisierung vermieden wird, 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 nachgestellten Wagenrücklauf eingegeben wird, bevor der Pfad festgelegt wird.