iOS7 之前,几乎所有的复杂文本都是通过 WebKit 来处理的,无论 UILabel、UITextField 还是 UITextView 都在后台以某种方式使用 webViews 来进行文本布局和渲染。iOS7 以后,苹果提供了 TextKit 对文本进行操作,并且对 UILabel、UITextField、UITextView 都利用 TextKit 进行了改造。
前言
TextKit 简介
TextKit是苹果提供的一系列用于文字排版服务的类和协议的集合,能够帮助 app 存储、布局和展示文本。TextKit 是以 CoreText 为基础构建的,所以能够提供与 CoreText 相同的性能和能力。下图展示了 TextKit 与 iOS 中其他文本和图形框架的相互关系:
TextKit 在界面展示方面提供给了开发者在文本渲染之上的全部权限。
TextKit 中主要的类
TextKit 中主要包括三个类:NSTextStorage、NSLayoutManager 以及 NSTextContainer,这三个主要的类与 view 之间的关系如下图:
-
NSTextStorage
是整个文本系统的数据来源,其管理着文本和文本属性信息,它是NSMutableAttributedString
的一个子类。 -
NSTextContainer
定义了一个可以布局文本的区域,并且也可以通过设置一个贝塞尔曲线数组的方式来为编辑区域设置排除区域,每个 Text View 都有一个 Text Container,它精确地描述了这个可用的区域。 -
NSLayoutManager
对文本系统的其他部分起到协调作用,将NSTextStorage
中存储的文本信息渲染到视图的展示区域。NSLayoutManager
的工作主要包括以下几个方面:监听 Text Storage 中文本或属性改变的通知,一旦接收到通知就触发布局进程;将NSTextStorage
中所有的字符翻译为字形;向它的 TextContainers 查询文本可用以绘制的区域;这些区域被行逐步填充,而行又被字形逐步填充。一旦一行填充完毕,下一行开始填充;对于每一行,布局管理器必须考虑断行行为(放不下的单词必须移到下一行)、连字符、内联的图像附件等等;当布局完成,文本的当前显示状态被设为无效,然后 Layout Manager 将前面几步排版好的文本设给 Text View。
TextKit 中各主要部分之间的关系如下图所示:
以编程的方式修改一个NSTextStorage
对象主要有三个阶段:
- 向
NSTextStorage
对象发送beginEditing
消息来开启接下来的一系列修改; - 通过
replaceCharactersInRange:withString:
和setAttributes:range:
方法来修改对象中存储的字符或者字符属性,每次在调用这些方法的时候,textStorage 对象都会自动的调用edited:range:changeInLength:
方法; - 修改结束后,发送
endEditing
消息,这样会使 textStorage 对象向代理发送消息
textStorage:willProcessEditing:range:changeInLength:
并且调用对象本身的processEditing
方法,之后向代理对象发送textStorage:didProcessEditing:range:changeInLength:
消息,最后, textStorage 对象会向其相关联的 layout manager 对象发送
processEditingForTextStorage:edited:range:changeInLength:invalidatedRange:
消息,所有相关联的 layout manager 会轮流通过这个消息重新计算字形位置。