摘录自Apple官方文档Text Programming Guide for iOS
字符:A character is the smallest unit of written language that carries meaning.
字形:Any one of these various concrete forms of a character is called a glyph.
字符和字形并没有一一对应的关系。某些情况下,字符可以由多个字形来表示。例如,“é”
由字形 “e”
和“´”
组合而成。另一种情况是,一个单独的字形可描绘出多个字符,例如,连写(ligature)。
Typefaces:A typeface is a set of visually related shapes for some or all of the characters in a written language.
Font:A font is a series of glyphs depicting(描绘) the characters in a consistent(一致的) size, typeface, and typestyle.
font family:A font family is a group of fonts that share a typeface but differ in typestyle.例如,Times是一个font family名。Times roman 和Times Italic是两个独立的fonts,都属于Times font family。
Fonts in the Times font family
Text Kit主要对象之间的关系如下图所示。Text views是UITextView
的实例,text containers是NSTextContainer
类的实例,layout manager是NSLayoutManager
类的实例。text storage是NSTextStorage
类的实例。在Text Kit中,一个NSTextStorage
对象存储UITextView
显示的text,使用NSLayoutManager
对象来布局到NSTextContainer
对象定义的区域。
NSTextContainer
定义了text布局的区域。通常,一个text container定义了一个矩形区域,但是,通过继承NSTextContainer
,你可以创建其它的形状:圆形,五角形,或者其它不规则形状。text container不仅定义了text填充区域的外形,而且还可以持有一个贝塞尔曲线的集合,用来指定不可填充文本的区域。
NSTextStorage
定义Text Kit最基本的存储机制。它继承自NSMutableAttributedString
。除了存储文本,NSTextStorage
还管理一系列NSLayoutManager
对象,通知它们字符或者熟悉改变了,来让它们重新显示文本。
NSLayoutManager
统筹处理text的操作。
Text Kit处理三种文本属性:character attributes,paragraph attributes和document attributes。字符属性包括字体、颜色和下标,作用于单个字符或者某个范围的字符。段落属性包括缩进,制表符(tabs),和行间距。文档属性包括页面大小、边距和缩放比例。
属性字符串在NSDictionary
对象中以键值对的方式来存储字符属性。
NSAttributedString
类提供了方法,通过一个字符索引,来返回相应的属性字典和属性应用的范围,例如attributesAtIndex:effectiveRange:.
NSTextStorage
对象使用NSMutableAttributedString
的方法addAttribute:value:range:在相应的字符范围添加属性。addAttributes:range:使用字典来添加属性。
为使用你自定义的属性,你需要继承NSLayoutManager
。子类需要重写drawGlyphsForGlyphRange:atPoint:方法。重写的方法首先调用父类的方法来绘制glyph range,然后再在上面绘制你自己的属性。另一种方式是,重写方法完全以你自己的方法来绘制字形。
NSParagraphStyle
类定义了段落属性。
虽然text system没有内置的存储文档属性的机制,NSAttributedString的初始化方法,initWithRTF:documentAttributes:
方法can populate an NSDictionary
object that you provide with document attributes derived from a stream of RTF
or HTML
data.相反的,ethods that write RTF data, such as RTFFromRange:documentAttributes:
, write document attributes if you pass a reference to an NSDictionary
object containing them with the message.
正在编辑的属性字符处可能会出现不一致,必须使用attribute fixing来清除。NSMutableAttributedString
定义了一个fixAttributesInRange:
方法来修正attachment,attachment和paragraph属性间的不一致。
编辑一个text storage对象有三个步骤。
第一阶段是发送一个beginEditing消息来声明一组变化(announce a group of changes)。
第二阶段是,发送一些编辑信息,例如 replaceCharactersInRange:withString:
和setAttributes:range:
。每次你发送这样的消息,text storage对象会调用edited:range:changeInLength:
来追踪受影响字符的范围。
第三阶段是,当完成改变text storage对象后,发送endEditing
消息。这会调用代理textStorage:willProcessEditing:range:changeInLength:
方法,并调用自己的processEditing
方法。
在修正属性后,text storage对象会给代理的textStorage:didProcessEditing:range:changeInLength:
方法发送消息,给代理一个机会来确认和改变属性。最后,text storage会每一个相关的layout manager发送processEditingForTextStorage:edited:range:changeInLength:invalidatedRange:
消息。layout managers使用这个消息来重新计算字形的位置,必要的话就重新显示。
NSLayoutManager
类是控制text显示的核心对象。它的作用如下:
NSTextStorage
相当于是模型,保存有字符和属性,如typeface, style, color, 和 size。NSTextContainer
也可被认为是模型的一部分,它被认为是在页面上text布局的几何形状模型。UITextView
(获取其他UIView
对象)提供view。NSLayoutManager
做控制器。
layout manager执行text布局有两个单独的步骤:生成字形(glyph generation)和布局字形(glyph layout)。
对于给定的一行,layout manager会给出一个矩形。然后会要求text container来调整矩形来适合。这个给出的矩形通常会超出text container的边界,但是它可以宽点或者窄点,它也可以完全或者部分的位于边框外。layout manager发送给text container来调整给出的矩形的消息是lineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect:,这个方法,会返回largest rectangle available for the proposed rectangle,基于text布局的方向。
当text最终适合矩形时,layout manager会做一个最终的调整。
下面简单的例子中,Text Kit对象被逐一的配置,一个text storage对象,一个text container,和一个layout manager。当一个text view从Interface Builder中拖出来后,这个配置就自动的被实例化。
也可用用代码来创建。
NSTextStorage* textStorage = [[NSTextStorage alloc] initWithString:string];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[textStorage addLayoutManager:layoutManager];
self.textContainer = [[NSTextContainer alloc] initWithSize:self.view.bounds.size];
[layoutManager addTextContainer:self.textContainer];
UITextView* textView = [[UITextView alloc] initWithFrame:self.view.bounds textContainer:self.textContainer];
[self.view addSubview:textView];
这个仅有一个text container和一个text view。
使用多个text containers,每个关联一个text view,就可以实现更复杂的布局。例如,支持分页
Object configuration for multicolumn text
不仅仅支持多个text container,还可以支持多个NSLayoutManager
对象。
Object configuration for multiple views of the same text