版本記錄
| 版本號(hào) | 時(shí)間 |
|---|---|
| V1.0 | 2018.09.01 |
前言
TextKit框架是對(duì)Core Text的封裝,用簡(jiǎn)潔的調(diào)用方式實(shí)現(xiàn)了大部分Core Text的功能。 TextKit是一個(gè)偏上層的開(kāi)發(fā)框架,在iOS7以上可用,使用它可以方便靈活處理復(fù)雜的文本布局,滿足開(kāi)發(fā)中對(duì)文本布局的各種復(fù)雜需求。TextKit實(shí)際上是基于CoreText的一個(gè)上層框架,其是面向?qū)ο蟮?。接下?lái)幾篇我們就一起看一下這個(gè)框架。感興趣的看下面幾篇文章。
1. TextKit框架詳細(xì)解析 (一) —— 基本概覽和應(yīng)用場(chǎng)景(一)
2. TextKit框架詳細(xì)解析 (二) —— 基本概覽和應(yīng)用場(chǎng)景(二)
3. TextKit框架詳細(xì)解析 (三) —— 一個(gè)簡(jiǎn)單布局示例(一)
4. TextKit框架詳細(xì)解析 (四) —— 一個(gè)簡(jiǎn)單布局示例(二)
5. TextKit框架詳細(xì)解析 (五) —— 文本編程指南之簡(jiǎn)介(一)
6. TextKit框架詳細(xì)解析 (六) —— 文本編程指南之展示文本內(nèi)容(二)
7. TextKit框架詳細(xì)解析 (七) —— 文本編程指南之排版概念(三)
8. TextKit框架詳細(xì)解析 (八) —— 文本編程指南之管理Text Fields and Text Views(四)
9. TextKit框架詳細(xì)解析 (九) —— 文本編程指南之管理鍵盤(五)
10. TextKit框架詳細(xì)解析 (十) —— 文本編程指南之復(fù)制、剪切和粘貼操作(六)
11. TextKit框架詳細(xì)解析 (十一) —— 文本編程指南之輸入數(shù)據(jù)的自定義視圖(七)
12. TextKit框架詳細(xì)解析 (十二) —— 文本編程指南之展示和管理編輯菜單(八)
Using Text Kit to Draw and Manage Text - 使用TextKit繪制和管理文本
UIKit框架包括幾個(gè)類,其目的是在應(yīng)用程序的用戶界面中顯示文本:UITextView,UITextField和UILabel,如在Displaying Text Content in iOS中所述。從UITextView類創(chuàng)建的文本視圖旨在顯示大量文本。底層UITextView是一個(gè)名為Text Kit的強(qiáng)大布局引擎。如果您需要自定義布局過(guò)程或需要干預(yù)該行為,則可以使用Text Kit。對(duì)于需要自定義解決方案的少量文本和特殊需求,您可以使用替代的底層技術(shù),如 Lower Level Text-Handling Technologies中所述。
Text Kit是UIKit框架中的一組類和協(xié)議,提供高質(zhì)量的排版服務(wù),使應(yīng)用程序能夠存儲(chǔ),布局和顯示具有精細(xì)排版的所有特征的文本,例如字距調(diào)整,連字,斷行和對(duì)齊。 Text Kit構(gòu)建于Core Text之上,因此它提供相同的速度和功能。 UITextView與Text Kit完全集成;它提供編輯和顯示功能,使用戶能夠輸入文本,指定格式屬性和查看結(jié)果。其他Text Kit類提供文本存儲(chǔ)和布局功能。圖8-1顯示了Text Kit在其他iOS文本和圖形框架中的位置。

Text Kit使您可以完全控制用戶界面元素中的文本呈現(xiàn)。 除了UITextView之外,UITextField和UILabel都構(gòu)建在Text Kit之上,它與動(dòng)畫,UICollectionView和UITableView無(wú)縫集成。 Text Kit采用完全可擴(kuò)展的面向?qū)ο蠹軜?gòu)設(shè)計(jì),支持子類化,代理和一組完整的通知,支持深度自定義。
Primary Text Kit Objects - 主要Text Kit對(duì)象
主要Text Kit對(duì)象之間的數(shù)據(jù)流路徑如圖8-2所示。 Text view是UITextView類的實(shí)例,文本容器是NSTextContainer類的實(shí)例,布局管理器是NSLayoutManager類的實(shí)例,文本存儲(chǔ)是NSTextStorage類的實(shí)例。 在Text Kit中,NSTextStorage對(duì)象存儲(chǔ)由UITextView對(duì)象顯示的文本,并由NSLayoutManager對(duì)象布置到由NSTextContainer對(duì)象定義的區(qū)域中。

NSTextContainer對(duì)象定義了可以布局文本的區(qū)域。通常,文本容器定義矩形區(qū)域,但通過(guò)創(chuàng)建NSTextContainer的子類,您可以創(chuàng)建其他形狀:例如圓形,五邊形或不規(guī)則形狀。文本容器不僅描述了可以用文本填充的區(qū)域的輪廓,還維護(hù)了一個(gè)Bezier路徑陣列,這些路徑是其區(qū)域內(nèi)沒(méi)有布局文本的禁區(qū)。在布局時(shí),文本圍繞排除路徑流動(dòng),提供了包含圖形和其他非文本布局元素的方法。
NSTextStorage定義了Text Kit擴(kuò)展文本處理系統(tǒng)的基本存儲(chǔ)機(jī)制。 NSTextStorage是NSMutableAttributedString的子類,用于存儲(chǔ)由文本系統(tǒng)操縱的字符和屬性。它確保文本和屬性在編輯操作中保持一致的狀態(tài)。除了存儲(chǔ)文本之外,NSTextStorage對(duì)象還管理一組客戶端NSLayoutManager對(duì)象,通知它們對(duì)其字符或?qū)傩缘娜魏胃?,以便它們可以根?jù)需要中繼和重新顯示文本。
NSLayoutManager對(duì)象編排其他文本處理對(duì)象的操作。它在將NSTextStorage對(duì)象中的數(shù)據(jù)轉(zhuǎn)換為視圖顯示區(qū)域中的渲染文本的操作中進(jìn)行干預(yù)。它將Unicode字符代碼映射到字形,并監(jiān)視由NSTextContainer對(duì)象定義的區(qū)域內(nèi)的字形布局。
注意:只要應(yīng)用程序保證從單個(gè)線程訪問(wèn),就可以從子線程訪問(wèn)
NLayoutManager,NSTextStorage和NSTextContainer。
有關(guān)UITextView的參考信息,請(qǐng)參閱UITextView Class Reference。 NSTextContainer在NSTextContainer Class Reference for iOS,NSLayoutManager在NSLayoutManager Class Reference for iOS和NSTextStorage在NSTextStorage Class Reference for iOS中描述。
Text Attributes - 文本屬性
Text Kit處理三種文本屬性:字符屬性,段落屬性和文檔屬性(character attributes, paragraph attributes, and document attributes)。 字符屬性包括諸如字體,顏色和下標(biāo)之類的特征,這些特征可以與單個(gè)字符或一系列字符相關(guān)聯(lián)。 段落屬性是縮進(jìn),制表符和行間距等特征。 文檔屬性包括文檔范圍的特征,如紙張大小,邊距和視圖縮放百分比。
1. Character Attributes - 字符屬性
屬性字符串將字符屬性存儲(chǔ)為NSDictionary對(duì)象中的鍵值對(duì)。 鍵是屬性名稱,由標(biāo)識(shí)符(NSString常量)表示,例如NSFontAttributeName。 圖8-3顯示了一個(gè)屬性字符串,其中屬性字典應(yīng)用于字符串中的范圍。

從概念上講,屬性字符串中的每個(gè)字符都有一個(gè)關(guān)聯(lián)的屬性字典。但是,通常,屬性字典適用于較長(zhǎng)范圍的字符,即一行文本。 NSAttributedString類提供了獲取字符索引并返回關(guān)聯(lián)屬性字典及其屬性值應(yīng)用范圍的方法,例如attributesAtIndex:effectiveRange:。
除了使用預(yù)定義屬性外,您還可以將您選擇的任何屬性鍵值對(duì)分配給一個(gè)區(qū)間的字符。使用NSMutableAttributedString方法addAttribute:value:range:將屬性添加到NSTextStorage對(duì)象中的相應(yīng)字符范圍。您還可以創(chuàng)建一個(gè)NSDictionary對(duì)象,其中包含一組自定義屬性的名稱和值,并使用addAttributes:range:方法在一個(gè)步驟中將它們添加到字符范圍。要使用自定義屬性,需要NSLayoutManager的自定義子類來(lái)使用它們。您的子類應(yīng)重寫[drawGlyphsForGlyphRange:atPoint:](https://developer.apple.com/documentation/appkit/nslayoutmanager/1403158-drawglyphsforglyphrange)方法。您的重寫可以先調(diào)用超類來(lái)繪制字形范圍,然后在頂部繪制自己的屬性。或者,您的重寫可以完全以您自己的方式繪制字形。
2. Paragraph Attributes - 段落屬性
段落屬性會(huì)影響布局管理器將文本行排列到頁(yè)面上的段落中的方式。 文本系統(tǒng)將段落屬性封裝在NSParagraphStyle類的對(duì)象中。 其中一個(gè)預(yù)定義字符屬性NSParagraphStyleAttributeName的值指向包含該字符范圍的段落屬性的NSParagraphStyle對(duì)象。 屬性修復(fù)確保只有一個(gè)NSParagraphStyle對(duì)象與每個(gè)段落中的字符相關(guān)。
段落屬性包括諸如對(duì)齊,制表位,換行模式和行間距(也稱為前導(dǎo))等特征。
3. Document Attributes - 文檔屬性
文檔屬性與文檔整體相關(guān)。 文檔屬性包括紙張大小,邊距和視圖縮放百分比等特征。 盡管文本系統(tǒng)沒(méi)有用于存儲(chǔ)文檔屬性的內(nèi)置機(jī)制,但NSAttributedString初始化方法(如initWithRTF:documentAttributes:)可以填充您提供的NSDictionary對(duì)象,其中包含從RTF或HTML數(shù)據(jù)流派生的文檔屬性。 相反,如果傳遞對(duì)包含消息的NSDictionary對(duì)象的引用,則編寫RTF數(shù)據(jù)的方法(如RTFFromRange:documentAttributes:)會(huì)寫入文檔屬性。
4. Attribute Fixing
編輯屬性字符串可能會(huì)導(dǎo)致必須通過(guò)attribute fixing來(lái)清除不一致性。 NSMutableAttributedString的UIKit擴(kuò)展定義了fixAttributesInRange:方法來(lái)修復(fù)附件,字符和段落屬性之間的不一致。 這些方法可確保在刪除附件字符后不會(huì)保留附件,字符屬性僅適用于該字體中可用的字符,并且段落屬性在各段落中都是一致的。
Changing Text Storage Programmatically - 以編程方式更改文本存儲(chǔ)
NSTextStorage對(duì)象充當(dāng)Text Kit的字符數(shù)據(jù)存儲(chǔ)庫(kù)。此數(shù)據(jù)的格式是屬性字符串attributed string,它是一系列字符(Unicode編碼)和相關(guān)屬性(如字體,顏色和段落樣式)。表示屬性字符串的類是NSAttributedString和NSMutableAttributedString,其中NSTextStorage是子類。如Character Attributes中所述,文本塊中的每個(gè)字符都有一個(gè)鍵和與之關(guān)聯(lián)的值的字典。鍵命名屬性(例如NSFontAttributeName),關(guān)聯(lián)值指定該屬性的特征(例如Helvetica 12-point)。
以編程方式編輯文本存儲(chǔ)對(duì)象有三個(gè)階段。第一階段是向它發(fā)送beginEditing消息以宣布一組更改。
在第二階段,您向它發(fā)送一些編輯消息,例如replaceCharactersInRange:withString:和setAttributes:range:,以實(shí)現(xiàn)字符或?qū)傩缘母?。每次發(fā)送這樣的消息時(shí),文本存儲(chǔ)對(duì)象都會(huì)調(diào)用edited:range:changeInLength:跟蹤自收到beginEditing消息以來(lái)受影響的字符范圍。
在第三階段,當(dāng)您完成文本存儲(chǔ)對(duì)象的更改后,將向其發(fā)送endEditing消息。這使它發(fā)出代理消息textStorage:willProcessEditing:range:changeInLength:并調(diào)用自己的processEditing方法,修復(fù)記錄的已更改字符范圍內(nèi)的屬性。有關(guān)屬性修復(fù)的信息,請(qǐng)參見(jiàn)Attribute Fixing。
在修復(fù)其屬性之后,文本存儲(chǔ)對(duì)象發(fā)送代理方textStorage:didProcessEditing:range:changeInLength:,為委托者提供驗(yàn)證并可能更改屬性的機(jī)會(huì)。 (盡管代理可以在此方法中更改文本存儲(chǔ)對(duì)象的字符屬性,但它不能在不使文本存儲(chǔ)處于不一致?tīng)顟B(tài)的情況下更改字符本身。)最后,文本存儲(chǔ)對(duì)象發(fā)送processEditingForTextStorage:edited:range:changeInLength:invalidatedRange:消息到每個(gè)關(guān)聯(lián)的布局管理器 - 指示文本存儲(chǔ)對(duì)象中已更改的范圍,以及這些更改的性質(zhì)。布局管理器反過(guò)來(lái)使用此信息重新計(jì)算其字形位置,并在必要時(shí)重新顯示。
Working with Font Objects - 使用字體對(duì)象
計(jì)算機(jī)字體是OpenType或TrueType等格式的數(shù)據(jù)文件,包含描述一組字形的信息,如 Characters and Glyphs中所述,以及字形渲染中使用的各種補(bǔ)充信息。 UIFont類提供了獲取和設(shè)置字體信息的接口。 UIFont實(shí)例提供對(duì)字體特征和字形的訪問(wèn)。 Text Kit將字符信息與字體信息組合在一起,以選擇文本布局期間使用的字形。您可以通過(guò)將字體對(duì)象傳遞給接受它們作為參數(shù)的方法來(lái)使用它們。字體對(duì)象是不可變的,因此可以安全地從應(yīng)用程序中的多個(gè)線程中使用它們。
您不使用alloc和init方法創(chuàng)建UIFont對(duì)象;相反,您使用preferredFontForTextStyle:文本樣式常量或fontWithName:size:。您還可以使用字體描述符使用fontWithDescriptor:size:來(lái)創(chuàng)建字體。這些方法檢查具有指定特征的現(xiàn)有字體對(duì)象,如果有,則返回它。否則,他們會(huì)查找請(qǐng)求的字體數(shù)據(jù)并創(chuàng)建相應(yīng)的字體對(duì)象。
1. Text Styles - 文本樣式
iOS 7中引入的文本樣式是字體的預(yù)期用途的語(yǔ)義描述,并通過(guò)稱為動(dòng)態(tài)類型(Dynamic Type)的機(jī)制實(shí)現(xiàn)。 文本樣式按使用組織,并由UIFontDescriptor.h中定義的常量表示,如表8-1所示。 用于由文本樣式描述的目的的實(shí)際字體可以基于許多動(dòng)態(tài)考慮因素而變化,包括用戶的內(nèi)容大小類別首選項(xiàng),其由UIApplication屬性preferredContentSizeCategory表示。 要獲取給定文本樣式的字體對(duì)象,請(qǐng)將相應(yīng)的常量傳遞給UIFont方法preferredFontForTextStyle:。 要獲取文本樣式的字體描述符,請(qǐng)將常量傳遞給UIFontDescriptor方法preferredFontDescriptorWithTextStyle:。 (有關(guān)字體描述符的更多信息,請(qǐng)參閱Using Font Descriptors。)

文本樣式通過(guò)動(dòng)態(tài)類型機(jī)制為應(yīng)用程序帶來(lái)許多優(yōu)勢(shì),所有這些都增強(qiáng)了文本的可讀性。動(dòng)態(tài)類型以協(xié)調(diào)的方式響應(yīng)用戶首選項(xiàng),并響應(yīng)可訪問(wèn)性設(shè)置以增強(qiáng)易讀性和超大類型。也就是說(shuō),當(dāng)您調(diào)用preferredFontForTextStyle:時(shí),返回的特定字體包括根據(jù)用戶首選項(xiàng)和上下文而變化的特征,包括跟蹤(字母間距)調(diào)整,以及針對(duì)特定文本樣式常量指定的用途進(jìn)行調(diào)整。
使用文本樣式常量返回的字體旨在用于應(yīng)用程序中除用戶界面元素中的文本之外的所有文本,例如buttons, bars, and labels。當(dāng)然,您需要選擇在您的應(yīng)用中看起來(lái)正確的文字樣式。觀察UIContentSizeCategoryDidChangeNotification也很重要,這樣您就可以在用戶更改內(nèi)容大小類別時(shí)重新布局文本。當(dāng)您的應(yīng)用收到該通知時(shí),它應(yīng)該將invalidateIntrinsicContentSize消息發(fā)送到由Auto Layout定位的視圖,或者將setNeedsLayout發(fā)送到手動(dòng)定位的用戶界面元素。它應(yīng)該使首選字體或字體描述符無(wú)效并根據(jù)需要獲取新字體。
2. Using Font Descriptors - 使用字體描述符
從UIFontDescriptor類實(shí)例化的字體描述符提供了一種使用屬性字典描述字體的方法,并用于創(chuàng)建UIFont對(duì)象。特別是,您可以從字體描述符創(chuàng)建UIFont對(duì)象,您可以從UIFont對(duì)象獲取描述符,并且您可以更改描述符并使用它來(lái)創(chuàng)建新的字體對(duì)象。您還可以使用字體描述符來(lái)指定應(yīng)用程序提供的自定義字體。
字體描述符可以存檔,這是使用文本樣式的一個(gè)優(yōu)點(diǎn)。您不應(yīng)該緩存由文本樣式指定的字體對(duì)象,因?yàn)樗鼈兪莿?dòng)態(tài)的 - 它們的特性會(huì)根據(jù)用戶首選項(xiàng)隨時(shí)間變化。但是您可以緩存字體描述符以保留字體的描述,然后在以后取消存檔并使用它來(lái)創(chuàng)建具有相同特征的字體對(duì)象。
您可以使用字體描述符在系統(tǒng)中查詢與特定屬性匹配的可用字體,然后創(chuàng)建與這些屬性匹配的字體實(shí)例,例如名稱,特征,語(yǔ)言和其他功能。例如,您可以使用字體描述符來(lái)檢索與給定字體系列名稱匹配的所有字體,使用CSS標(biāo)準(zhǔn)定義的系列名稱,如Listing 8-1所示。
// Listing 8-1 Font family name matching
UIFontDescriptor *helveticaNeueFamily =
[UIFontDescriptor fontDescriptorWithFontAttributes:
@{ UIFontDescriptorFamilyAttribute: @"Helvetica Neue" }];
NSArray *matches =
[helveticaNeueFamily matchingFontDescriptorsWithMandatoryKeys: nil];
如上所示,matchingFontDescriptorsWithMandatoryKeys:方法返回系統(tǒng)中所有Helvetica Neue字體的字體描述符數(shù)組,例如HelveticaNeue,HelveticaNeue-Medium,HelveticaNeue-Light,HelveticaNeue-Thin等。
您可以通過(guò)應(yīng)用符號(hào)特征,例如粗體,斜體,展開(kāi)和濃縮修改preferredFontForTextStyle:返回的字體。 您可以使用字體描述符來(lái)修改特定的特征,如Listing 8-2所示。
// Listing 8-2 Font trait modification
UIFontDescriptor *fontDescriptor =
[UIFontDescriptor preferredFontDescriptorWithTextStyle: UIFontTextStyleBody];
UIFontDescriptor *boldFontDescriptor =
[fontDescriptor fontDescriptorWithSymbolicTraits: UIFontDescriptorTraitBold];
UIFont *boldFont = [UIFont fontWithDescriptor: boldFontDescriptor size: 0.0];
此代碼段首先檢索正文文本樣式的字體描述符,然后修改該字體描述符以指定粗體特征,最后使用UIFont類方法fontWithDescriptor:size:以粗體返回正文文本樣式的實(shí)際字體對(duì)象特征。 使用fontWithDescriptor:size:傳遞size大小值為0.0指定保留最初使用字體描述符返回的size屬性。 當(dāng)然,這種行為是必需的,因?yàn)樽煮w大小由動(dòng)態(tài)類型機(jī)制決定。
3. Activating Font Features - 激活字體特征
字體描述符的另一個(gè)重要用途是激活和選擇字體特征。字體特征是字體的排版屬性,當(dāng)字體由文本系統(tǒng)呈現(xiàn)時(shí)控制其外觀的各個(gè)方面。僅當(dāng)字體設(shè)計(jì)者選擇包含字體時(shí),字體特征才可用于字體。某些字體特征以少數(shù)字體提供,而其他字體則適用于許多字體。此外,安裝在不同平臺(tái)上的相同字體的不同版本可以在其可用字體特征方面有所不同。
字體特征分為稱為feature types的類別,其中單個(gè)特征選擇器選擇特定特征設(shè)置。特征類型可以是獨(dú)占的,也可以是非獨(dú)占的。如果特征類型是獨(dú)占的,則您一次只能選擇一個(gè)可用的特征選擇器,例如數(shù)字是比例還是固定寬度。如果特征類型為非排他性,則可以一次啟用任意數(shù)量的特征選擇器。例如,對(duì)于連字特征類型,您可以選擇字體支持的可用連字類的任意組合。
注意:如果選擇字體中不可用的特征,則不會(huì)看到字體字形外觀發(fā)生變化。
一些特征是上下文的,而另一些特征是非上下文的。將上下文特征應(yīng)用于字形的方式取決于字形與相鄰字形相比的位置。文本系統(tǒng)布局功能的一個(gè)強(qiáng)大方面是它能夠自動(dòng)應(yīng)用復(fù)雜的上下文處理。
無(wú)論相鄰的字形如何,非上下文特征(Noncontextual features )都以相同的方式應(yīng)用于字形。這些特征包括選擇替代字形集,以便為文本提供不同的外觀和字形替換,以達(dá)到數(shù)學(xué)排版或增強(qiáng)排版復(fù)雜性的目的。
例如,Listing 8-3中的代碼激活了Helvetica Neue Medium字體定義的兩種特征類型。
// Listing 8-3 Activating Font Features
NSArray *timeFeatureSettings = @[
@{
UIFontFeatureTypeIdentifierKey: @(kNumberSpacingType),
UIFontFeatureSelectorIdentifierKey: @(kProportionalNumbersSelector)
},
@{
UIFontFeatureTypeIdentifierKey: @(kCharacterAlternativesType),
UIFontFeatureSelectorIdentifierKey: @(2)
}];
UIFont *originalFont = [NSFont fontWithName: @"HelveticaNeue-Medium" size: 12.0];
UIFontDescriptor *originalDescriptor = [originalFont fontDescriptor];
UIFontDescriptor *timeDescriptor = [originalDescriptor
fontDescriptorByAddingAttributes: @{
UIFontDescriptorFeatureSettingsAttribute: timeFeatureSettings }];
UIFont *timeFont = [UIFont fontWithDescriptor: timeDescriptor size: 12.0];
Listing 8-3中的代碼激活數(shù)字間距特征類型(由常量kNumberSpacingType表示),選擇比例寬度數(shù)字(kProportionalNumbersSelector)和字符替代特征類型(kCharacterAlternativesType),其中特征選擇器標(biāo)識(shí)符鍵值為2。 此示例中用于表示字體特征類型和選擇器的常量在Core Text框架(CoreText / CoreText.h)中的頭文件SFNTLayoutTypes.h中聲明為枚舉。 對(duì)于字符替換類型,沒(méi)有預(yù)定義常量來(lái)表示特征選擇器標(biāo)識(shí)符,因此您只需使用字體定義的數(shù)值。
由于字體功能是由字體定義的,因此確定支持特征的最可靠方法是直接查詢字體。 您可以使用Core Text中的CTFontCopyFeatures函數(shù)執(zhí)行此操作,如Listing 8-4所示。
// Listing 8-4 Querying Font Features
UIFont *font = [UIFont fontWithName: @"HelveticaNeue-Medium" size: 12.0];
CFArrayRef fontFeatures = CTFontCopyFeatures((__bridge CTFontRef) font);
NSLog(@"properties = %@", fontFeatures);
Listing 8-5顯示了Listing 8-4中CTFontCopyFeatures函數(shù)產(chǎn)生的字體特征數(shù)組,因?yàn)樗@示在控制臺(tái)日志中。
// Listing 8-5 Typical Result from CTFontCopyFeatures Function
properties = (
{
CTFeatureTypeExclusive = 1;
CTFeatureTypeIdentifier = 6;
CTFeatureTypeName = "Number Spacing";
CTFeatureTypeNameID = 266;
CTFeatureTypeSelectors = (
{
CTFeatureSelectorDefault = 1;
CTFeatureSelectorIdentifier = 0;
CTFeatureSelectorName = "No Change";
CTFeatureSelectorNameID = 264;
},
{
CTFeatureSelectorIdentifier = 1;
CTFeatureSelectorName = "Proportional Numbers";
CTFeatureSelectorNameID = 267;
}
);
},
{
CTFeatureTypeExclusive = 1;
CTFeatureTypeIdentifier = 17;
CTFeatureTypeName = "Character Alternatives";
CTFeatureTypeNameID = 262;
CTFeatureTypeSelectors = (
{
CTFeatureSelectorDefault = 1;
CTFeatureSelectorIdentifier = 0;
CTFeatureSelectorName = "No Change";
CTFeatureSelectorNameID = 264;
},
{
CTFeatureSelectorIdentifier = 1;
CTFeatureSelectorName = "Alternate Punctuation";
CTFeatureSelectorNameID = 263;
},
{
CTFeatureSelectorIdentifier = 2;
CTFeatureSelectorName = "Numbers Punctuation";
CTFeatureSelectorNameID = 265;
}
);
}
)
在這種情況下,結(jié)果顯示此版本的Helvetica Neue Medium字體具有兩種字體特征:Number Spacing和Character Alternatives。使用字體描述符激活字體特征并在其設(shè)置中進(jìn)行選擇時(shí),此結(jié)果中最重要的值是特征類型標(biāo)識(shí)符和特征選擇器標(biāo)識(shí)符。將這些值添加到表示字體特征設(shè)置的字典數(shù)組中,然后將該數(shù)組用作UIFontDescriptorFeatureSettingsAttribute的值,并依次傳遞給fontDescriptorByAddingAttributes:或fontDescriptorWithFontAttributes:方法,如Listing 8-3所示。該列表中顯示的常量的枚舉值與CTFontCopyFeatures函數(shù)返回的特征類型標(biāo)識(shí)符和特征選擇器標(biāo)識(shí)符的數(shù)值相關(guān)聯(lián)。
如Listing 8-5所示,CTFontCopyFeatures函數(shù)生??成的字體特征數(shù)組還顯示特征類型是否為獨(dú)占,以及哪個(gè)特征selector是默認(rèn)值。當(dāng)然,功能類型名稱和功能selector名稱值為可用的字體特征及其設(shè)置提供了人類可讀的標(biāo)識(shí)。
4. Querying Font Metrics - 查詢字體度量標(biāo)準(zhǔn)
當(dāng)該信息可用時(shí),UIFont定義了許多用于訪問(wèn)字體度量信息的方法。 諸如ascender, capHeight, xHeight等屬性都對(duì)應(yīng)于標(biāo)準(zhǔn)字體度量信息。 圖8-4顯示了字體度量如何應(yīng)用于字形維度,表8-2列出了與各種度量相關(guān)的屬性名稱。 有關(guān)更多具體信息,請(qǐng)參閱屬性說(shuō)明。


Laying Out Text - 布局文本
從NSLayoutManager類實(shí)例化的布局管理器對(duì)象是Text Kit中文本顯示的中央控制對(duì)象。布局管理器執(zhí)行以下操作:
- 控制文本存儲(chǔ)和文本容器對(duì)象
- 從字符生成字形
- 計(jì)算字形位置并存儲(chǔ)信息
- 管理字形和字符的范圍
- 視圖請(qǐng)求時(shí)在文本視圖中繪制字形
- 計(jì)算文本行的邊界框矩形
- 控制連字符
- 處理字符屬性和字形屬性
在模型 - 視圖 - 控制器范例中,布局管理器是控制器。 NSTextStorage是NSMutableAttributedString的子類,它提供了模型的一部分,包含一串文本字符,其中包含字體,樣式,顏色和大小等屬性。 NSTextContainer也可以被視為模型的一部分,因?yàn)樗M了布局文本的頁(yè)面的幾何布局。 UITextView(或其他UIView對(duì)象)提供顯示文本的視圖。 NSLayoutManager用作文本系統(tǒng)的控制器,因?yàn)樗鼘⑽谋敬鎯?chǔ)對(duì)象中的字符轉(zhuǎn)換為字形,根據(jù)一個(gè)或多個(gè)文本容器對(duì)象的尺寸將它們排成行,并在一個(gè)或多個(gè)文本視圖對(duì)象中協(xié)調(diào)文本顯示。
1. The Layout Process - 布局過(guò)程
布局管理器在兩個(gè)單獨(dú)的步驟中執(zhí)行文本布局:字形生成和字形布局。布局管理器懶地執(zhí)行兩個(gè)布局步驟,即根據(jù)需要執(zhí)行。因此,一些NSLayoutManager方法會(huì)導(dǎo)致字形生成,而其他方法則不會(huì),字形布局也是如此。在生成字形并在計(jì)算其布局位置之后,布局管理器會(huì)緩存信息以提高后續(xù)調(diào)用的性能。
布局管理器緩存字形,屬性和布局信息。它跟蹤文本存儲(chǔ)中字符更改失效的字形范圍。有兩種方法可以自動(dòng)使字符范圍無(wú)效:如果需要生成字形或者需要字符布局。如果您愿意,可以手動(dòng)使字形或布局信息無(wú)效。當(dāng)布局管理器收到需要知道字形或布局在無(wú)效范圍內(nèi)的消息時(shí),它會(huì)生成字形或根據(jù)需要重新計(jì)算布局。
2. Generating Line Fragment Rectangles - 生成線段矩形
布局管理器在字形行中的NSTextContainer對(duì)象中布局文本。文本容器中這些行的布局由其形狀和它包含的任何排除路徑確定。無(wú)論線段矩形與由排除路徑定義的區(qū)域相交,這些部分中的線都必須縮短或分段;如果整個(gè)區(qū)域之間存在間隙,則必須移動(dòng)與其重疊的線以進(jìn)行補(bǔ)償。
布局管理器為給定的行提出一個(gè)矩形,然后要求文本容器調(diào)整矩形以適應(yīng)。建議的矩形通常跨越文本容器的邊界矩形,但它可以更窄或更寬,并且它也可以部分或完全位于邊界矩形之外。布局管理器發(fā)送文本容器以調(diào)整建議的矩形的消息是lineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect:,它根據(jù)文本的布局方向返回可用于建議矩形的最大矩形。它還返回一個(gè)包含任何剩余空間的矩形,例如文本容器中孔或間隙另一側(cè)留下的空間。
布局管理器在實(shí)際將文本插入矩形時(shí)進(jìn)行最后一次調(diào)整。這個(gè)調(diào)整是由文本容器固定的一小部分,稱為行片段填充(line fragment padding),它將行片段矩形每端的部分定義為空白。文本在行片段矩形內(nèi)插入此量(矩形本身不受影響)。填充允許在邊緣(以及任何孔周圍)對(duì)文本容器區(qū)域進(jìn)行小規(guī)模調(diào)整,并使文本不直接鄰接區(qū)域附近顯示的任何其他圖形。您可以使用lineFragmentPadding屬性更改默認(rèn)值的填充。請(qǐng)注意,線段填充不是表達(dá)邊距的合適方法。對(duì)于文檔邊距,應(yīng)在其封閉視圖中設(shè)置UITextView對(duì)象的位置和大小。對(duì)于文本邊距,您應(yīng)該設(shè)置文本視圖的textContainerInset屬性。此外,您可以使用NSMutableParagraphStyle屬性(如headIndent)為各個(gè)段落設(shè)置縮進(jìn)值。
除了返回線段矩形本身之外,布局管理器還返回一個(gè)稱為使用矩形(used rectangle)的矩形。這是線段矩形的一部分,實(shí)際上包含要繪制的字形或其他標(biāo)記。按照慣例,兩個(gè)矩形都包括行片段填充和行間距(根據(jù)字體的行高度度量和段落的行間距參數(shù)計(jì)算)。但是,段落間距(前后)和文本周圍添加的任何空格(例如由中心間距文本引起的空間)僅包含在線段矩形中,并且不包含在使用的矩形中。
3. Specifying Exclusion Paths - 指定排除路徑
文本容器維護(hù)一個(gè)UIBezierPath對(duì)象數(shù)組,表示接收器邊界矩形內(nèi)的排除路徑。 當(dāng)布局管理器向文本容器發(fā)送lineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect:消息,建議與排除路徑定義的某個(gè)區(qū)域相交的線段矩形時(shí),文本容器返回一個(gè)不包括該區(qū)域的調(diào)整后的線段矩形。 該過(guò)程如圖8-6所示。

4. Specifying Multipage and Multicolumn Layouts - 指定多頁(yè)和多列布局
在最簡(jiǎn)單的情況下,Text Kit對(duì)象是單獨(dú)配置的,即一個(gè)文本存儲(chǔ)對(duì)象,一個(gè)文本容器和一個(gè)布局管理器,如圖8-6所示。 從Interface Builder中的對(duì)象庫(kù)拖動(dòng)文本視圖時(shí),將自動(dòng)實(shí)例化此配置。 UITextView對(duì)象會(huì)檢出其他對(duì)象并將它們連接在一起。 您還可以在代碼中創(chuàng)建此排列,如Listing 8-6所示。

您還可以在代碼中創(chuàng)建此排列,如Listing 8-6所示。 此代碼可以位于視圖控制器中,例如,UIViewController的子類,它具有名為textContainer的NSTextContainer屬性。
// Listing 8-6 Object creation for a single text flow
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];
僅具有一個(gè)文本容器和一個(gè)文本視圖限制了此配置。 在這種安排中,文本在由文本容器定義的區(qū)域內(nèi)不間斷地流動(dòng)。 這種安排無(wú)法滿足分頁(yè)符,多列布局和更復(fù)雜的布局。
通過(guò)使用多個(gè)文本容器,每個(gè)容器具有關(guān)聯(lián)的文本視圖,可以進(jìn)行更復(fù)雜的布局安排。 例如,要支持分頁(yè)符,應(yīng)用程序可以配置文本對(duì)象,如圖8-7所示。

每個(gè)文本容器對(duì)應(yīng)于文檔的頁(yè)面。 顯示文本的視圖可以嵌入到應(yīng)用程序提供的自定義視圖對(duì)象中,作為文本視圖的背景。 反過(guò)來(lái),此自定義視圖可以嵌入到UIScrollView對(duì)象中,以使用戶能夠滾動(dòng)文檔的頁(yè)面。
多列文檔可以使用類似的對(duì)象排列建模,如圖8-8所示。

現(xiàn)在有兩個(gè)文本容器 - 一個(gè)用于頁(yè)面上的每一列,而不是讓一個(gè)文本容器對(duì)應(yīng)一個(gè)頁(yè)面。每個(gè)文本容器控制文檔的一部分。顯示文本時(shí),首先在左上角的容器中布置字形。當(dāng)該視圖中沒(méi)有空間時(shí),布局管理器通知其代理它已完成填充容器。代理可以檢查是否有更多需要布局的文本,并在必要時(shí)添加另一個(gè)文本容器。布局管理器繼續(xù)在下一個(gè)容器中布置文本,在完成時(shí)通知代理,依此類推。同樣,自定義視圖(描繪為藍(lán)色矩形)為這些文本列提供了畫布。
您不僅可以擁有多個(gè)文本容器,還可以擁有多個(gè)訪問(wèn)同一文本存儲(chǔ)的NSLayoutManager對(duì)象。圖8-9說(shuō)明了具有多個(gè)布局管理器的對(duì)象布局。這種安排的效果是提供相同文本的多個(gè)視圖。如果用戶更改頂視圖中的文本,則更改會(huì)立即反映在底部視圖中(假設(shè)更改的位置在底部視圖的邊界內(nèi))。

后記
本篇主要講述了使用TextKit繪制和管理文本,感興趣的給個(gè)贊或者關(guān)注~~~
