TextKit en Xamarin. iOSTextKit in Xamarin.iOS

TextKit es una nueva API que ofrece características de representación y diseño de texto eficaces.TextKit is a new API that offers powerful text layout and rendering features. Se basa en el marco de texto básico de nivel inferior, pero es mucho más fácil de usar que el texto principal.It is built on top of the low-level Core Text framework, but is much easier to use than Core Text.

Para que las características de TextKit estén disponibles para los controles estándar, se han vuelto a implementar varios controles de texto de iOS para usar TextKit, entre los que se incluyen:To make the features of TextKit available to standard controls, several iOS text controls have been re-implemented to use TextKit, including:

  • UITextViewUITextView
  • UITextFieldUITextField
  • UILabelUILabel

ArquitecturaArchitecture

TextKit proporciona una arquitectura en capas que separa el almacenamiento de texto del diseño y la presentación, incluidas las clases siguientes:TextKit provides a layered architecture that separates the text storage from the layout and display, including the following classes:

  • NSTextContainer: proporciona el sistema de coordenadas y la geometría que se usan para el diseño de texto.NSTextContainer – Provides the coordinate system and geometry that is used to layout text.
  • NSLayoutManager: diseña texto convirtiéndolo en glifos.NSLayoutManager – Lays out text by turning text into glyphs.
  • NSTextStorage: contiene los datos de texto, así como las actualizaciones de las propiedades de texto por lotes.NSTextStorage – Holds the text data, as well as handles batch text property updates. Todas las actualizaciones por lotes se entregan al administrador de diseño para el procesamiento real de los cambios, como volver a calcular el diseño y volver a dibujar el texto.Any batch updates are handed to the layout manager for the actual processing of the changes, such as recalculating the layout and redrawing the text.

Estas tres clases se aplican a una vista que representa el texto.These three classes are applied to a view that renders text. Las vistas de control de texto integradas, como UITextView, UITextFieldy UILabel ya las tienen establecidas, pero también se pueden crear y aplicar a cualquier instancia de UIView.The built-in text handling views, such as UITextView, UITextField, and UILabel already have them set, but you can create and apply them to any UIView instance as well.

En la ilustración siguiente se muestra esta arquitectura:The following figure illustrates this architecture:

Atributos y almacenamiento de textoText Storage and Attributes

La clase NSTextStorage contiene el texto que se muestra en una vista.The NSTextStorage class holds the text that is displayed by a view. También se comunican los cambios realizados en el texto, como los cambios en los caracteres o sus atributos, en el administrador de diseño para su presentación.It also communicates any changes to the text - such as changes to characters or their attributes - to the layout manager for display. NSTextStorage hereda de MSMutableAttributed cadena, lo que permite especificar los cambios en los atributos de texto en lotes entre las llamadas a BeginEditing y EndEditing.NSTextStorage inherits from MSMutableAttributed string, allowing changes to text attributes to be specified in batches between BeginEditing and EndEditing calls.

Por ejemplo, el siguiente fragmento de código especifica un cambio en los colores de primer plano y de fondo, respectivamente, y tiene como destino intervalos determinados:For example, the following code snippet specifies a change to the foreground and background colors, respectively, and targets particular ranges:

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

Una vez que se llama a EndEditing, los cambios se envían al administrador de diseño, que a su vez realiza los cálculos de diseño y representación necesarios para el texto que se va a mostrar en la vista.After EndEditing is called, the changes are sent to the layout manager, which in turn performs any necessary layout and rendering calculations for the text to be displayed in the view.

Diseño con ruta de exclusiónLayout with Exclusion Path

TextKit también admite el diseño y permite escenarios complejos como texto de varias columnas y flujo de texto alrededor de rutas de acceso especificadas denominadas rutas de exclusión.TextKit also supports layout, and allows for complex scenarios such as multi-column text and flowing text around specified paths called exclusion paths. Las rutas de exclusión se aplican al contenedor de texto, que modifica la geometría del diseño del texto, lo que hace que el texto fluya alrededor de las rutas de acceso especificadas.Exclusion paths are applied to the text container, which modifies the geometry of the text layout, causing the text to flow around the specified paths.

Para agregar una ruta de exclusión es necesario establecer la propiedad ExclusionPaths en el administrador de diseño.Adding an exclusion path requires setting the ExclusionPaths property on the layout manager. Establecer esta propiedad hace que el administrador de diseño invalide el diseño del texto y fluya el texto alrededor de la ruta de exclusión.Setting this property causes the layout manager to invalidate the text layout and flow the text around the exclusion path.

Exclusión basada en un CGPathExclusion based on a CGPath

Considere la siguiente implementación de subclase UITextView:Consider the following UITextView subclass implementation:

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

Este código agrega compatibilidad para dibujar en la vista de texto mediante gráficos principales.This code adds support for drawing on the text view using Core Graphics. Dado que la clase UITextView se ha creado para usar TextKit para su representación y presentación de texto, puede usar todas las características de TextKit, como establecer rutas de exclusión.Since the UITextView class is now built to use TextKit for its text rendering and layout, it can use all the features of TextKit, such as setting exclusion paths.

Importante

En este ejemplo se subclases UITextView para agregar compatibilidad con el dibujo táctil.This example subclasses UITextView to add touch drawing support. La subclase UITextView no es necesaria para obtener las características de TextKit.Subclassing UITextView isn’t necessary to get the features of TextKit.

Después de que el usuario dibuje en la vista de texto, la CGPath dibujada se aplica a una instancia de UIBezierPath mediante el establecimiento de la propiedad UIBezierPath.CGPath:After the user draws on the text view, the drawn CGPath is applied to a UIBezierPath instance by setting the UIBezierPath.CGPath property:

bezierPath.CGPath = exclusionPath;

La actualización de la siguiente línea de código hace que el diseño de texto se actualice en torno a la ruta de acceso:Updating the following line of code makes the text layout update around the path:

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

En la captura de pantalla siguiente se muestra cómo cambia el diseño del texto para fluir en torno a la ruta de acceso dibujada:The following screenshot illustrates how the text layout changes to flow around the drawn path:

Observe que en este caso la propiedad AllowsNonContiguousLayout del administrador de diseño está establecida en false.Notice that the layout manager’s AllowsNonContiguousLayout property is set to false in this case. Esto hace que el diseño se vuelva a calcular para todos los casos en los que el texto cambie.This causes the layout to be recalculated for all cases where the text changes. Si se establece en true, el rendimiento puede mejorar si se evita una actualización de diseño completo, especialmente en el caso de documentos grandes.Setting this to true may benefit performance by avoiding a full-layout refresh, especially in the case of large documents. Sin embargo, si se establece AllowsNonContiguousLayout en true, se evitará que la ruta de exclusión actualice el diseño en algunas circunstancias, por ejemplo, si el texto se escribe en tiempo de ejecución sin un retorno de carro final antes de que se establezca la ruta de acceso.However, setting AllowsNonContiguousLayout to true would prevent the exclusion path from updating the layout in some circumstances - for example, if text is entered at runtime without a trailing carriage return prior to the path being set.