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, UITextFieldund UILabel sind bereits festgelegt, aber Sie können sie auch erstellen und auf alle UIView instance anwenden.

Die folgende Abbildung veranschaulicht diese Architektur:

Diese Abbildung veranschaulicht die TextKit-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:

Dieser 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.