TextKit в Xamarin.iOS
TextKit — это новый API, который предлагает мощные функции макета текста и отрисовки. Он построен на основе низкоуровневой базовой текстовой платформы, но гораздо проще использовать, чем основной текст.
Чтобы сделать функции TextKit доступными для стандартных элементов управления, несколько элементов управления текстом iOS были повторно реализованы для использования TextKit, в том числе:
- UITextView
- UITextField
- UILabel
Архитектура
TextKit предоставляет многоуровневую архитектуру, которая отделяет текстовое хранилище от макета и отображения, включая следующие классы:
NSTextContainer
— предоставляет систему координат и геометрию, которая используется для макета текста.NSLayoutManager
— выкладывает текст, превратив текст в глифы.NSTextStorage
— содержит текстовые данные, а также обрабатывает обновления свойств пакетного текста. Все пакетные обновления передаются диспетчеру макетов для фактической обработки изменений, таких как пересчет макета и перерасчет текста.
Эти три класса применяются к представлению, которое отрисовывает текст. Встроенные представления обработки текста, такие как UITextView
UITextField
, и UILabel
уже имеют их набор, но их можно создать и применить к любому UIView
экземпляру.
На следующем рисунке показана архитектура:
Текстовые служба хранилища и атрибуты
Класс NSTextStorage
содержит текст, отображаемый представлением. Он также сообщает о любых изменениях текста, таких как изменения символов или их атрибутов, в диспетчер макетов для отображения. NSTextStorage
наследует от MSMutableAttributed
строки, что позволяет изменять текстовые атрибуты в пакетах между BeginEditing
вызовами.EndEditing
Например, следующий фрагмент кода указывает изменение на передний план и цвета фона соответственно и целевые диапазоны.
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 ();
После EndEditing
вызова изменения отправляются в диспетчер макетов, который, в свою очередь, выполняет все необходимые вычисления макета и отрисовки для отображения текста в представлении.
Макет с путем исключения
TextKit также поддерживает макет и позволяет выполнять сложные сценарии, такие как текст с несколькими столбцами и поток текста вокруг указанных путей, называемых путями исключения. Пути исключения применяются к текстовому контейнеру, который изменяет геометрию текстового макета, что приводит к потоку текста вокруг указанных путей.
Для добавления пути исключения требуется задать ExclusionPaths
свойство в диспетчере макетов. Задание этого свойства приводит к тому, что диспетчер макетов отменяет макет текста и перемещает текст вокруг пути исключения.
Исключение на основе CGPath
Рассмотрим следующую 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);
}
}
}
}
Этот код добавляет поддержку рисования в текстовом представлении с помощью основной графики. UITextView
Так как класс теперь создан для использования TextKit для отрисовки текста и макета, он может использовать все функции TextKit, такие как настройка путей исключения.
Внимание
В этом примере подклассы для добавления поддержки UITextView
сенсорного рисования. Подклассы UITextView
не необходимы для получения функций TextKit.
После того как пользователь рисует текстовое представление, он CGPath
применяется к UIBezierPath
экземпляру, задав UIBezierPath.CGPath
свойство:
bezierPath.CGPath = exclusionPath;
Обновление следующей строки кода приводит к обновлению текстового макета по пути:
TextContainer.ExclusionPaths = new UIBezierPath[] { bezierPath };
На следующем снимке экрана показано, как изменяется макет текста для обхода нарисованного пути:
Обратите внимание, что свойство диспетчера макетов AllowsNonContiguousLayout
имеет значение false в этом случае. Это приводит к перерасчету макета во всех случаях, когда текст изменяется. Если задать значение true, это может повысить производительность, избегая полного обновления макета, особенно в случае больших документов. Однако если задано AllowsNonContiguousLayout
значение true, то путь исключения не будет обновлять макет в некоторых случаях, например если текст вводится во время выполнения без конечного возврата каретки до заданного пути.