TextKit dans Xamarin.iOS

TextKit est une nouvelle API qui offre de puissantes fonctionnalités de disposition et de rendu de texte. Il s’appuie sur l’infrastructure texte de base de bas niveau, mais il est beaucoup plus facile à utiliser que le texte principal.

Pour rendre les fonctionnalités de TextKit disponibles pour les contrôles standard, plusieurs contrôles de texte iOS ont été réinscrits pour utiliser TextKit, notamment :

  • UITextView
  • UITextField
  • UILabel

Architecture

TextKit fournit une architecture en couches qui sépare le stockage de texte de la disposition et de l’affichage, y compris les classes suivantes :

  • NSTextContainer : fournit le système de coordonnées et la géométrie utilisés pour la disposition du texte.
  • NSLayoutManager : met en place le texte en transformant le texte en glyphes.
  • NSTextStorage : contient les données de texte et gère les mises à jour des propriétés de texte par lot. Toutes les mises à jour par lots sont transmises au gestionnaire de disposition pour le traitement réel des modifications, comme le recalcul de la disposition et le redessinage du texte.

Ces trois classes sont appliquées à une vue qui affiche le texte. Les vues de gestion de texte intégrées, telles que UITextView, UITextFieldet UILabel les ont déjà définies, mais vous pouvez également les créer et les appliquer à n’importe quel UIView instance.

La figure suivante illustre cette architecture :

Cette figure illustre l’architecture TextKit

Stockage de texte et attributs

La NSTextStorage classe contient le texte affiché par une vue. Il communique également toutes les modifications apportées au texte (par exemple, les modifications apportées aux caractères ou à leurs attributs) au gestionnaire de disposition pour l’affichage. NSTextStoragehérite de MSMutableAttributed la chaîne, ce qui permet de spécifier les modifications apportées aux attributs de texte dans les lots entre BeginEditing les appels et .EndEditing

Par exemple, l’extrait de code suivant spécifie une modification des couleurs de premier plan et d’arrière-plan, respectivement, et cible des plages particulières :

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

Après EndEditing avoir été appelées, les modifications sont envoyées au gestionnaire de disposition, qui effectue à son tour les calculs de disposition et de rendu nécessaires pour que le texte soit affiché dans la vue.

Disposition avec chemin d’exclusion

TextKit prend également en charge la disposition et permet des scénarios complexes tels que le texte à plusieurs colonnes et le texte circulant autour des chemins d’accès spécifiés appelés chemins d’exclusion. Les chemins d’exclusion sont appliqués au conteneur de texte, ce qui modifie la géométrie de la disposition du texte, ce qui entraîne la circulation du texte autour des chemins spécifiés.

L’ajout d’un chemin d’exclusion nécessite la définition de la ExclusionPaths propriété sur le gestionnaire de disposition. La définition de cette propriété entraîne l’invalidation de la disposition du texte par le gestionnaire de disposition et le déplacement du texte autour du chemin d’exclusion.

Exclusion basée sur un CGPath

Envisagez l’implémentation de la sous-classe suivante UITextView :

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

Ce code ajoute la prise en charge du dessin sur l’affichage texte à l’aide de Core Graphics. Étant donné que la UITextView classe est désormais conçue pour utiliser TextKit pour son rendu et sa disposition de texte, elle peut utiliser toutes les fonctionnalités de TextKit, telles que la définition de chemins d’exclusion.

Important

Cet exemple de sous-classes UITextView pour ajouter la prise en charge du dessin tactile. La sous-classe UITextView n’est pas nécessaire pour obtenir les fonctionnalités de TextKit.

Une fois que l’utilisateur dessine sur la vue texte, le dessin CGPath est appliqué à un UIBezierPath instance en définissant la UIBezierPath.CGPath propriété :

bezierPath.CGPath = exclusionPath;

La mise à jour de la ligne de code suivante met à jour la disposition du texte autour du chemin d’accès :

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

La capture d’écran suivante montre comment la disposition du texte change pour circuler dans le chemin dessiné :

Cette capture d’écran montre comment la disposition du texte change pour circuler dans le chemin dessiné

Notez que la propriété du gestionnaire de AllowsNonContiguousLayout disposition a la valeur false dans ce cas. Cela entraîne le recalcul de la disposition pour tous les cas où le texte est modifié. La définition de cette valeur sur true peut améliorer les performances en évitant une actualisation complète de la mise en page, en particulier dans le cas de documents volumineux. Toutefois, la définition de la valeur AllowsNonContiguousLayout true empêche le chemin d’exclusion de mettre à jour la disposition dans certaines circonstances, par exemple, si du texte est entré au moment de l’exécution sans retour chariot de fin avant la définition du chemin d’accès.