iOS中文本控件的底层是TextKit,它能够精细地排版与布局文本
TextKit是对CoreText的封装(如下图),所以有同样的性能
在TextKit中,NSTextStorage存储了显示在TextViews中的string,文本的布局是由NSLayoutManager来处理的,文本的布局区域由于NSTextContainer定义
NSTextContainer:
NSTextContainer定义了一个区域,文本在该区域内布局,一般是矩形,也可以创建NSTextContainer的子类来自定义为其他形状
NSTextStorage:
NSTextStorage是NSMutableAttributedString的子类 ,存储着string和Attribute,你可以把它看做TextKit的数据库,另外它还管理者一组NSLayoutManager对象,因为当文本改变的时候需要通知NSLayoutManager
NSLayoutManager:
NSLayoutManager管理NSTextStorage和NSTextContainer,它负责把NSTextStorage中的文本显示出来,把字符转为字形,管理布局
2.Text Attributes
Text Kit可以处理三种文本属性:字符属性(Character Attributes),段落属性(Paragraph Attributes)和文档属性(Document Attributes)。
2.1字符属性(Character Attributes)
addAttribute:value:range://你可以用该方法给文本添加属性
drawGlyphsForGlyphRange:atPoint//如果需要自定义属性,就需要实现NSLayoutManager子类并且重写该方法
2.1字符属性(Paragraph Attributes)
很简单: 使用NSParagraphStyle添加段落属性
3.1文档属性(Document Attributes)
initWithRTF:documentAttributes://文本系统没有其他的机制存储文档属性,就提供了该方法初始化的时候设置
改变Text Storage
//一共3步
1.`[beginEditing]`
2.改变文本属性,比如`setAttributes:range:`,当执行完第一步后,
//每当你改变文本属性的时候,
//text storage对象都会调用edited:range:changeInLength:方法来跟踪修改
3.`[endEditing]`//这时候text storage会
//调用代理方法[textStorage:willProcessEditing:range:changeInLength:],
//然后调用它自己的[processEditing](fixing attributes within the recorded range of changed characters)
//再调用代理[textStorage:didProcessEditing:range:changeInLength:]给代理一个机会验证或改变文本属性
//最后发送[processEditingForTextStorage:edited:range:changeInLength:invalidatedRange:]给所有的layout manager,layout managers用这些信息重新计算字形的位置,重新显示
3.Laying out Text
NSLayoutManager是TextKit的中央控制系统,在MVC中,NSLayoutManager就是C, NSTextStorage是M, NSTextContainer其实也是M,因为它存储了视图显示的区域, UITextView是V, NSLayoutManager干以下事情:
- 控制text storage 和text container
- 从characters生成glyphs
- 计算字形的位置并且保存信息
- 管理字形和字符的范围
- 在text view中绘制glyphs
- 计算文本行的边框矩形
- 控制连字符
- 操作字符属性和字形属性
4.The Layout Process
Layout Manager执行布局分为2步:字形生成和字形布局,
5.Generating Line Fragment Rectangles
layout manager 会使用[lineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect:]方法像text container 获得一个矩形区域用来放置文本(Line Fragment Rectangles),然后会根据lineFragmentPadding属性来调整该矩形区域的padding(不要使用该属性来调整整个文档的边距),
除了返回Line Fragment Rectangles还会返回一个叫做used rectangle的矩形区域,这两个矩形都包括了lineFragmentPadding和interline space,但是段落间距仅仅包含在Line Fragment Rectangles中,used rectangle没有