TextKit w środowisku Xamarin.iOS

TextKit to nowy interfejs API, który oferuje zaawansowany układ tekstu i funkcje renderowania. Jest on oparty na strukturze tekstu podstawowego niskiego poziomu, ale jest znacznie łatwiejszy w użyciu niż podstawowy tekst.

Aby udostępnić funkcje zestawu TextKit do standardowych kontrolek, kilka kontrolek tekstu systemu iOS zostało ponownie zaimplementowanych w celu użycia zestawu TextKit, w tym:

  • UITextView
  • UITextField
  • UILabel

Architektura

TextKit udostępnia architekturę warstwową, która oddziela magazyn tekstu od układu i wyświetlania, w tym następujące klasy:

  • NSTextContainer — udostępnia układ współrzędnych i geometrię używaną do układu tekstu.
  • NSLayoutManager — Określa tekst, zamieniając tekst na glify.
  • NSTextStorage — przechowuje dane tekstowe, a także obsługuje aktualizacje właściwości tekstu wsadowego. Wszystkie aktualizacje wsadowe są przekazywane do menedżera układu w celu rzeczywistego przetwarzania zmian, takich jak ponowne obliczanie układu i ponowne rysowanie tekstu.

Te trzy klasy są stosowane do widoku, który renderuje tekst. Wbudowane widoki obsługi tekstu, takie jak UITextView, UITextFieldi UILabel mają już ustawione, ale można je również utworzyć i zastosować do dowolnego UIView wystąpienia.

Na poniższej ilustracji przedstawiono tę architekturę:

This figure illustrates the TextKit architecture

Magazyn tekstu i atrybuty

Klasa NSTextStorage przechowuje tekst wyświetlany przez widok. Komunikuje również wszelkie zmiany w tekście — takie jak zmiany znaków lub ich atrybutów — do menedżera układu na potrzeby wyświetlania. NSTextStorage dziedziczy z MSMutableAttributed ciągu, co umożliwia określenie zmian atrybutów tekstowych w partiach między wywołaniami BeginEditing i EndEditing .

Na przykład poniższy fragment kodu określa zmianę odpowiednio kolorów pierwszego planu i tła oraz elementów docelowych dla określonych zakresów:

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 ();

Po EndEditing wywołaniu zmiany są wysyłane do menedżera układu, co z kolei wykonuje wszelkie niezbędne obliczenia układu i renderowania tekstu, które mają być wyświetlane w widoku.

Układ ze ścieżką wykluczenia

Zestaw TextKit obsługuje również układ i umożliwia wykonywanie złożonych scenariuszy, takich jak tekst wielokolumny i przepływ tekstu wokół określonych ścieżek nazywanych ścieżkami wykluczeń. Ścieżki wykluczeń są stosowane do kontenera tekstowego, który modyfikuje geometrię układu tekstu, co powoduje przepływ tekstu wokół określonych ścieżek.

Dodanie ścieżki wykluczenia wymaga ustawienia ExclusionPaths właściwości w menedżerze układu. Ustawienie tej właściwości powoduje, że menedżer układu unieważnia układ tekstu i przepływa tekstu wokół ścieżki wykluczenia.

Wykluczenie oparte na CGPath

Rozważmy następującą UITextView implementację podklasy:

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);
            }
        }
    }
}

Ten kod dodaje obsługę rysowania w widoku tekstowym przy użyciu podstawowej grafiki. UITextView Ponieważ klasa jest teraz kompilowana do używania zestawu TextKit do renderowania i układu tekstu, może używać wszystkich funkcji zestawu TextKit, takich jak ustawianie ścieżek wykluczeń.

Ważne

Te przykładowe podklasy UITextView do dodawania obsługi rysunku dotykowego. Podklasy UITextView nie jest konieczne, aby uzyskać funkcje zestawu TextKit.

Gdy użytkownik narysuje widok tekstu, zostanie CGPath zastosowany do UIBezierPath wystąpienia, ustawiając UIBezierPath.CGPath właściwość :

bezierPath.CGPath = exclusionPath;

Aktualizacja następującego wiersza kodu powoduje, że układ tekstu jest aktualizowany wokół ścieżki:

TextContainer.ExclusionPaths = new UIBezierPath[] { bezierPath };

Na poniższym zrzucie ekranu pokazano, jak układ tekstu zmienia się w przepływie wokół narysowanej ścieżki:

This screenshot illustrates how the text layout changes to flow around the drawn path

Zwróć uwagę, że właściwość menedżera układów AllowsNonContiguousLayout jest ustawiona na wartość false w tym przypadku. Powoduje to ponowne obliczenie układu we wszystkich przypadkach, w których zmienia się tekst. Ustawienie wartości true może przynieść wydajność, unikając pełnego odświeżania układu, zwłaszcza w przypadku dużych dokumentów. Jednak ustawienie AllowsNonContiguousLayout wartości true uniemożliwiłoby zaktualizowanie układu przez ścieżkę wykluczenia w pewnych okolicznościach — na przykład jeśli tekst zostanie wprowadzony w czasie wykonywania bez końcowego powrotu karetki przed ustawieniem ścieżki.