TextKit no Xamarin.iOS
O TextKit é uma nova API que oferece recursos poderosos de layout e renderização de texto. Ele é construído sobre a estrutura de baixo nível Core Text, mas é muito mais fácil de usar do que o Core Text.
Para tornar os recursos do TextKit disponíveis para controles padrão, vários controles de texto do iOS foram reimplementados para usar o TextKit, incluindo:
- UITextView
- UITextField
- UILabel
Arquitetura
O TextKit fornece uma arquitetura em camadas que separa o armazenamento de texto do layout e da exibição, incluindo as seguintes classes:
NSTextContainer
– Fornece o sistema de coordenadas e geometria que é usado para layout de texto.NSLayoutManager
– Estabelece texto transformando texto em glifos.NSTextStorage
– Mantém os dados de texto, bem como manipula atualizações de propriedades de texto em lote. Todas as atualizações em lote são entregues ao gerenciador de layout para o processamento real das alterações, como recalcular o layout e redesenhar o texto.
Essas três classes são aplicadas a um modo de exibição que renderiza texto. Os modos de exibição internos de manipulação de texto, como UITextView
, UITextField
e UILabel
já os têm definidos, mas você também pode criá-los e aplicá-los a qualquer UIView
instância.
A figura a seguir ilustra essa arquitetura:
Armazenamento de texto e atributos
A NSTextStorage
classe contém o texto que é exibido por um modo de exibição. Ele também comunica quaisquer alterações no texto - como alterações em caracteres ou seus atributos - ao gerenciador de layout para exibição. NSTextStorage
herda da cadeia de caracteres, permitindo que as alterações nos atributos de MSMutableAttributed
texto sejam especificadas em lotes entre BeginEditing
e EndEditing
chamadas.
Por exemplo, o trecho de código a seguir especifica uma alteração nas cores de primeiro plano e plano de fundo, respectivamente, e destina-se a intervalos específicos:
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 ();
Depois EndEditing
de chamado, as alterações são enviadas para o gerenciador de layout, que por sua vez executa todos os cálculos de layout e renderização necessários para que o texto seja exibido na exibição.
Layout com caminho de exclusão
O TextKit também oferece suporte a layout e permite cenários complexos, como texto de várias colunas e texto fluindo em caminhos especificados chamados caminhos de exclusão. Os caminhos de exclusão são aplicados ao contêiner de texto, que modifica a geometria do layout de texto, fazendo com que o texto flua em torno dos caminhos especificados.
A adição de um caminho de exclusão requer a configuração da ExclusionPaths
propriedade no gerenciador de layout. A definição dessa propriedade faz com que o gerenciador de layout invalide o layout de texto e flua o texto ao redor do caminho de exclusão.
Exclusão com base em um CGPath
Considere a seguinte UITextView
implementação de subclasse:
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);
}
}
}
}
Esse código adiciona suporte para desenho no modo de exibição de texto usando elementos gráficos principais. Como a classe agora foi criada para usar o UITextView
TextKit para sua renderização e layout de texto, ela pode usar todos os recursos do TextKit, como definir caminhos de exclusão.
Importante
Este exemplo subclasses UITextView
para adicionar suporte a desenho por toque. A subclassificação UITextView
não é necessária para obter os recursos do TextKit.
Depois que o usuário desenha no modo de exibição de texto, o desenho CGPath
é aplicado a uma UIBezierPath
instância definindo a UIBezierPath.CGPath
propriedade:
bezierPath.CGPath = exclusionPath;
A atualização da seguinte linha de código faz com que o layout de texto seja atualizado ao redor do caminho:
TextContainer.ExclusionPaths = new UIBezierPath[] { bezierPath };
A captura de tela a seguir ilustra como o layout do texto muda para fluir ao redor do caminho desenhado:
Observe que a propriedade do gerenciador de AllowsNonContiguousLayout
layout é definida como false nesse caso. Isso faz com que o layout seja recalculado para todos os casos em que o texto é alterado. Definir isso como true pode beneficiar o desempenho, evitando uma atualização de layout completo, especialmente no caso de documentos grandes. No entanto, definir AllowsNonContiguousLayout
como true impediria que o caminho de exclusão atualizasse o layout em algumas circunstâncias - por exemplo, se o texto for inserido em tempo de execução sem um retorno de carro à direita antes do caminho ser definido.