Xamarin.ios의 TextKitTextKit in Xamarin.iOS

TextKit은 강력한 텍스트 레이아웃 및 렌더링 기능을 제공 하는 새로운 API입니다.TextKit is a new API that offers powerful text layout and rendering features. 하위 수준 핵심 텍스트 프레임 워크를 기반으로 구축 되었지만 핵심 텍스트 보다 훨씬 더 쉽게 사용할 수 있습니다.It is built on top of the low-level Core Text framework, but is much easier to use than Core Text.

TextKit의 기능을 표준 컨트롤에서 사용할 수 있도록 하기 위해 다음을 비롯 하 여 TextKit를 사용 하도록 몇 가지 iOS 텍스트 컨트롤이 다시 구현 되었습니다.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

아키텍처Architecture

TextKit는 다음 클래스를 비롯 하 여 레이아웃과 표시에서 텍스트 저장소를 구분 하는 계층화 된 아키텍처를 제공 합니다.TextKit provides a layered architecture that separates the text storage from the layout and display, including the following classes:

  • NSTextContainer – 텍스트 레이아웃에 사용 되는 좌표계 및 기 하 도형을 제공 합니다.NSTextContainer – Provides the coordinate system and geometry that is used to layout text.
  • NSLayoutManager – 텍스트를 문자 모양으로 전환 하 여 텍스트를 배치 합니다.NSLayoutManager – Lays out text by turning text into glyphs.
  • NSTextStorage – 텍스트 데이터를 포함 하 고 일괄 처리 텍스트 속성 업데이트를 처리 합니다.NSTextStorage – Holds the text data, as well as handles batch text property updates. 일괄 처리 업데이트는 레이아웃을 다시 계산 하 고 텍스트를 다시 그리는 등의 실제 변경 내용 처리를 위해 레이아웃 관리자에 전달 됩니다.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.

이러한 세 클래스는 텍스트를 렌더링 하는 뷰에 적용 됩니다.These three classes are applied to a view that renders text. UITextView, UITextFieldUILabel와 같은 기본 제공 텍스트 처리 뷰는 이미 설정 되어 있지만 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.

다음 그림은이 아키텍처를 보여 줍니다.The following figure illustrates this architecture:

텍스트 저장소 및 특성Text Storage and Attributes

NSTextStorage 클래스에는 뷰에 표시 되는 텍스트가 포함 됩니다.The NSTextStorage class holds the text that is displayed by a view. 또한 텍스트에 대 한 변경 내용 (예: 문자 또는 특성의 변경)을 레이아웃 관리자에 게 표시 하기 위해 전달 합니다.It also communicates any changes to the text - such as changes to characters or their attributes - to the layout manager for display. NSTextStorageMSMutableAttributed 문자열에서 상속 되므로 BeginEditingEndEditing 호출 사이에 일괄 처리로 텍스트 특성을 지정할 수 있습니다.NSTextStorage inherits from MSMutableAttributed string, allowing changes to text attributes to be specified in batches between BeginEditing and EndEditing calls.

예를 들어, 다음 코드 조각에서는 전경색과 배경색을 각각 변경 하 고 특정 범위를 대상으로 지정 합니다.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 ();

EndEditing가 호출 되 면 변경 내용이 레이아웃 관리자에 게 전송 되 고,이는 뷰에 표시 되는 텍스트에 필요한 레이아웃과 렌더링 계산을 수행 합니다.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.

제외 경로를 포함 하는 레이아웃Layout with Exclusion Path

TextKit는 레이아웃도 지원 하며, 여러 열 텍스트와 같은 복잡 한 시나리오와 제외 경로라는 지정 된 경로 주위에 텍스트 흐름이 가능 합니다.TextKit also supports layout, and allows for complex scenarios such as multi-column text and flowing text around specified paths called exclusion paths. 텍스트 컨테이너에 제외 경로를 적용 하 여 텍스트 레이아웃의 기 하 도형을 수정 하면 지정 된 경로 주위에 텍스트가 흐르게 됩니다.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.

제외 경로를 추가 하려면 레이아웃 관리자에서 ExclusionPaths 속성을 설정 해야 합니다.Adding an exclusion path requires setting the ExclusionPaths property on the layout manager. 이 속성을 설정 하면 레이아웃 관리자가 텍스트 레이아웃을 무효화 하 고 제외 경로 주위에서 텍스트를 흐르게 됩니다.Setting this property causes the layout manager to invalidate the text layout and flow the text around the exclusion path.

CGPath 기반 제외Exclusion based on a CGPath

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

이 코드는 핵심 그래픽을 사용 하 여 텍스트 뷰에 그리기 위한 지원을 추가 합니다.This code adds support for drawing on the text view using Core Graphics. UITextView 클래스는 이제 텍스트 렌더링 및 레이아웃에 대해 TextKit를 사용 하도록 빌드 되었으므로 제외 경로 설정과 같은 TextKit의 모든 기능을 사용할 수 있습니다.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.

중요

이 예제 서브 클래스 UITextView 터치 그리기 지원을 추가 합니다.This example subclasses UITextView to add touch drawing support. TextKit의 기능을 얻기 위해 UITextView 서브클래싱은 필요 하지 않습니다.Subclassing UITextView isn’t necessary to get the features of TextKit.

사용자가 텍스트 뷰를 그린 후에는 UIBezierPath.CGPath 속성을 설정 하 여 그린 CGPath UIBezierPath 인스턴스에 적용 됩니다.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;

다음 코드 줄을 업데이트 하면 경로 주위에 텍스트 레이아웃 업데이트가 수행 됩니다.Updating the following line of code makes the text layout update around the path:

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

다음 스크린 샷에서는 그려지는 경로를 중심으로 텍스트 레이아웃을 변경 하는 방법을 보여 줍니다.The following screenshot illustrates how the text layout changes to flow around the drawn path:

이 경우 레이아웃 관리자의 AllowsNonContiguousLayout 속성이 false로 설정 되어 있습니다.Notice that the layout manager’s AllowsNonContiguousLayout property is set to false in this case. 이렇게 하면 텍스트가 변경 되는 모든 경우에 레이아웃을 다시 계산 합니다.This causes the layout to be recalculated for all cases where the text changes. 이를 true로 설정 하면 전체 레이아웃 새로 고침을 방지 하 여 성능이 향상 될 수 있습니다. 특히 문서가 큰 경우에는 더욱 그렇습니다.Setting this to true may benefit performance by avoiding a full-layout refresh, especially in the case of large documents. 그러나 AllowsNonContiguousLayout를 true로 설정 하면 일부 환경에서 제외 경로에서 레이아웃을 업데이트할 수 없습니다. 예를 들어, 경로를 설정 하기 전에 후행 캐리지 리턴 없이 런타임에 텍스트를 입력 하는 경우입니다.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.