iOS中文本控件的底層是TextKit,它能夠精細地排版與布局文本
TextKit是對CoreText的封裝(如下圖),所以有同樣的性能
在TextKit中,NSTextStorage存儲了顯示在TextViews中的string,文本的布局是由NSLayoutManager來處理的,文本的布局區(qū)域由于NSTextContainer定義
NSTextContainer:
NSTextContainer定義了一個區(qū)域,文本在該區(qū)域內(nèi)布局,一般是矩形,也可以創(chuàng)建NSTextContainer的子類來自定義為其他形狀
NSTextStorage:
NSTextStorage是NSMutableAttributedString的子類 ,存儲著string和Attribute,你可以把它看做TextKit的數(shù)據(jù)庫,另外它還管理者一組NSLayoutManager對象,因為當文本改變的時候需要通知NSLayoutManager
NSLayoutManager:
NSLayoutManager管理NSTextStorage和NSTextContainer,它負責把NSTextStorage中的文本顯示出來,把字符轉(zhuǎn)為字形,管理布局
2.Text Attributes
Text Kit可以處理三種文本屬性:字符屬性(Character Attributes),段落屬性(Paragraph Attributes)和文檔屬性(Document Attributes)聋丝。
2.1字符屬性(Character Attributes)
addAttribute:value:range://你可以用該方法給文本添加屬性
drawGlyphsForGlyphRange:atPoint//如果需要自定義屬性,就需要實現(xiàn)NSLayoutManager子類并且重寫該方法
2.1字符屬性(Paragraph Attributes)
很簡單: 使用NSParagraphStyle添加段落屬性
3.1文檔屬性(Document Attributes)
initWithRTF:documentAttributes://文本系統(tǒng)沒有其他的機制存儲文檔屬性,就提供了該方法初始化的時候設置
改變Text Storage
//一共3步
1.`[beginEditing]`
2.改變文本屬性,比如`setAttributes:range:`,當執(zhí)行完第一步后,
//每當你改變文本屬性的時候,
//text storage對象都會調(diào)用edited:range:changeInLength:方法來跟蹤修改
3.`[endEditing]`//這時候text storage會
//調(diào)用代理方法[textStorage:willProcessEditing:range:changeInLength:],
//然后調(diào)用它自己的[processEditing](fixing attributes within the recorded range of changed characters)
//再調(diào)用代理[textStorage:didProcessEditing:range:changeInLength:]給代理一個機會驗證或改變文本屬性
//最后發(fā)送[processEditingForTextStorage:edited:range:changeInLength:invalidatedRange:]給所有的layout manager,layout managers用這些信息重新計算字形的位置,重新顯示
3.Laying out Text
NSLayoutManager是TextKit的中央控制系統(tǒng),在MVC中,NSLayoutManager就是C, NSTextStorage是M, NSTextContainer其實也是M,因為它存儲了視圖顯示的區(qū)域, UITextView是V, NSLayoutManager干以下事情:
- 控制text storage 和text container
- 從characters生成glyphs
- 計算字形的位置并且保存信息
- 管理字形和字符的范圍
- 在text view中繪制glyphs
- 計算文本行的邊框矩形
- 控制連字符
- 操作字符屬性和字形屬性
4.The Layout Process
Layout Manager執(zhí)行布局分為2步:字形生成和字形布局,
5.Generating Line Fragment Rectangles
layout manager 會使用[lineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect:]方法像text container 獲得一個矩形區(qū)域用來放置文本(Line Fragment Rectangles),然后會根據(jù)lineFragmentPadding屬性來調(diào)整該矩形區(qū)域的padding(不要使用該屬性來調(diào)整整個文檔的邊距),
除了返回Line Fragment Rectangles還會返回一個叫做used rectangle的矩形區(qū)域,這兩個矩形都包括了lineFragmentPadding和interline space,但是段落間距僅僅包含在Line Fragment Rectangles中,used rectangle沒有