本文只是讓自己加深下理解,方便以后查看,原作者看到有不合適的地方,或者大神們看到有不對(duì)的地方希望指正 ~
原文地址,尊重原作者 ~
<br />
一. 特點(diǎn)和用法
關(guān)于YYText的特點(diǎn)和用法請(qǐng)看@ibireme大神的github
二. 使用到的組件
介紹YYLabel之前先說(shuō)一下
YYTextAttribute,因?yàn)楹竺鏁?huì)大量的使用到它。
YYTextAttribute定義的一些
Enum,
YYTextAttributeType:attribute的類型,有None、UIKit、CoreText和YYText四種類型;
YYTextLineStyle:line的樣式;
YYTextVerticalAlignment: 垂直方向text的位置;
YYTextDirection:text的位置;
YYTextTruncationType:text截?cái)嗟奈恢谩?/p>YYText中定義的
Attribute Name。主要是獨(dú)有的一些類型。YYTextBackedString:可以將一些表情圖片映射成純文本。
YYTextBinding:使一些特定的字符串綁定在一起,
YYTextView在選擇和編輯他們的時(shí)候把他們當(dāng)成一個(gè)單獨(dú)的字符。YYTextShadow:用處和
NSShadow一樣,只是比NSShadow多了一些功能,比如說(shuō)可以使用blendMode(圖形混合模式)、可以在shadow上再加一層shadow。關(guān)于blendMode的學(xué)習(xí),可以參見(jiàn)喵神的博客YYTextDecoration:實(shí)現(xiàn)下劃線(underline)和中間截線(strikethrough)時(shí)使用,線條的形式給出了幾種樣式,可以通過(guò)YYTextLineStyle枚舉查看。具體是
underline還是strikethrough是在NSAttributedString+YYText中NSMutableAttributeString(YYText)中實(shí)現(xiàn)的方法。YYTextBorder:實(shí)現(xiàn)在文本周圍畫一個(gè)border,也可以是填充一個(gè)背景色。
YYTextAttachment:封裝需要放入
text中的對(duì)象。在說(shuō)明文檔中提到,如果attachment是UIImage,就繪制到CGContext,如果是UIView或者CALayer就加入到text container的view或者layer中。YYTextHightlight:當(dāng)
YYLabel或者YYTextView中的text可以被用戶按下時(shí),被按下的text會(huì)有一個(gè)highlighted狀態(tài),這時(shí)候就需要是用YYTextHighlight來(lái)修改原來(lái)的text。所以這個(gè)對(duì)象和YYText一樣,只是是在highlight狀態(tài)下的YYText,而且添加了點(diǎn)擊和長(zhǎng)按事件。再來(lái)說(shuō)一下NSAttributedString+YYText文件
在這個(gè)分類中主要是實(shí)現(xiàn)了幾類的操作:一些操作當(dāng)前
attributed string的方法
比如說(shuō)歸檔和反歸檔當(dāng)前字符串、獲得某個(gè)位置的attributes、字間距、色值、背景色、shadow等等。具體的參見(jiàn)文件,基本上是作者封裝的方便獲得各種數(shù)據(jù)的方法。(和Foundation比強(qiáng)大太多了)。為YYText創(chuàng)建
attachment的方法為YYText添加YYText特有的
attribute的方法添加像設(shè)置屬性一樣的設(shè)置
character attribute的font、color、backgroundColor等等的方法添加像設(shè)置屬性一樣的設(shè)置
paragraph attribute的方法。添加像設(shè)置屬性一樣的設(shè)置YYText
attribute的方法。使用
range設(shè)置不連續(xù)的attribute的方法設(shè)置
text highlight的便捷方法和其他的工具型的方法。
NSParagraphStyle+YYText文件
提供了CoreText中的CTParagraphStyleRef和NSParagraphStyle之間的轉(zhuǎn)化-
YYTextParser
這是一個(gè)protocol,聲明了一個(gè)-(BOOL)parseText:(NSMutableAttributedString *)string selectedRange:(NSRangePointer)selectedRange;方法。這個(gè)方式是遵守這個(gè)協(xié)議必須實(shí)現(xiàn)的方法,當(dāng)YYTextView或者YYLabel中的text改變時(shí)被調(diào)用。返回YES說(shuō)明修改了這個(gè)text。
作者簡(jiǎn)單的實(shí)現(xiàn)了MarkdownParser和EmotionParser,兩個(gè)原理都差不多一樣,在這里只對(duì)EmotionParser做一下簡(jiǎn)單的介紹,希望能有所啟發(fā):
這段代碼(上圖)就是生成正則表達(dá)式_regex和映射的字典_mapper,第一層for是獲得你要匹配的key,第二層是如果有這些特殊的字符需要轉(zhuǎn)譯一下,然后將這些需要比配的key用“|”連接起來(lái)。
這個(gè)(上圖)就是修改text之后會(huì)被調(diào)用的方法,在這個(gè)方法里對(duì)輸入的text進(jìn)行匹配,如果匹配到之前_mapper中需要替換的字符,就將這個(gè)字符串替換為需要替換的表情符。
替換成表情符之后就需要重新計(jì)算這個(gè)表情符所占的range了,這個(gè)方法就是拿到替換之后的的range。
5.YYTextLayout
先看一下文檔中的說(shuō)明,如下圖:

是不是很眼熟?好像在哪見(jiàn)過(guò)?是的,就是NSLayoutManager和NSTextContainer。他們的作用都是相似的。
-
YYTextContainer
支持矩形(CGSize)和圖形(UIBezierPath)來(lái)初始化YYTextContainer;
在這里重點(diǎn)說(shuō)一下YYTextLinePositionModifier,它是 一個(gè)協(xié)議,定義了一個(gè)必須實(shí)現(xiàn)的方法,這個(gè)方法將會(huì)在layout完成的時(shí)候被調(diào)用,三個(gè)參數(shù)分別是存放YYTextLine的數(shù)組、完整的text和layout container。
YYTextLine:它是封裝了CTLineRef的對(duì)象,封裝了每一行text的具體展示位置、range、這一行擁有的attachments等等,只有一個(gè)類方法的初始化方法。如果不了解一些自行描述集的內(nèi)容,對(duì)textLine中的一些屬性和操作會(huì)不是很清晰,看下圖:
邊框(Bounding Box):一個(gè)假想的邊框,盡可能地容納整個(gè)字形。
基線(Baseline):一條假想的參照線,以此為基礎(chǔ)進(jìn)行字形的渲染。一般來(lái)說(shuō)是一條橫線。
基礎(chǔ)原點(diǎn)(Origin):基線上最左側(cè)的點(diǎn)。
行間距(Leading):行與行之間的間距。
字間距(Kerning):字與字之間的距離,為了排版的美觀,并不是所有的字形之間的距離都是一致的,但是這個(gè)基本步影響到我們的文字排版。
上行高度(Ascent)和下行高度(Decent):一個(gè)字形最高點(diǎn)和最低點(diǎn)到基線的距離,所以行高就是ascent + decent。
看完上面的簡(jiǎn)單介紹你就能明白,在YYTextLine的setCTLine中的代碼邏輯是從CTLineRef中取出對(duì)應(yīng)的行寬、上行高度、下行高度、行間距、rangge和第一個(gè)字型符的位置(這個(gè)在垂直布局會(huì)用到)。之后調(diào)用reloadBounds方法,重新計(jì)算當(dāng)前行的bounds、attachments所在的range和rect。 -
YYTextLayout
這個(gè)真的是核心內(nèi)容了,這個(gè)文件一共3300多行的代碼,從代碼量上就能看出它的地位。這個(gè)類中存儲(chǔ)著text的layout結(jié)果,所有的property都是readonly的。實(shí)現(xiàn)的接口有:
1、通過(guò)一些類方法初始化的方法(YYTextContainer、CGSize和text)
2、layout之后的attributes,都是只讀的
3、從layout中讀取信息(位置、range等等)
4、繪制text layout
這個(gè)類主要是使用上面講過(guò)的所有的數(shù)據(jù)來(lái)繪制text,這部分的代碼還是需要一點(diǎn)一點(diǎn)的去讀的,如果是新手每一行都會(huì)有收獲(比如說(shuō)我),如果是老司機(jī)就沒(méi)有必要一行行的讀了,了解他的解題思路和解決這個(gè)問(wèn)題的辦法就可以。下面說(shuō)一下生成layout的那個(gè)500行代碼的情況,就按照代碼的順序從上往下大概的說(shuō)明一下干了什么。
* 1)、初始化一系列使用到的變量
2)、安全判斷,text和container
3)、判斷是否需要修復(fù)emoji的bug(iOS8.3中)
4)、判斷是否設(shè)置了path屬性和exclusionPaths數(shù)組,做相應(yīng)的計(jì)算拿到CGPath,如果CGPath為空則goto fail 返回nil(如果設(shè)置了path,size和insets就沒(méi)有用了)
5)、判斷是不是奇偶填充,判斷pathWidth是否為0,判斷是否是垂直展示
6)、使用text創(chuàng)建CTFramesetterRef,創(chuàng)建失敗goto fail
7)、使用上一步中創(chuàng)建的frameSetter創(chuàng)建CTFrameRef
8)、從CTFrameRef的對(duì)象中獲得每一行、ctRun數(shù)組,計(jì)算每一行的frame,判斷是否實(shí)現(xiàn)了linePositionModifier這個(gè)協(xié)議,有的話調(diào)用協(xié)議方法。
9)、計(jì)算bounding size
10)、判斷是否需要truncation,和按那種方式處理
11)、判斷是否垂直布局text,需要的話,旋轉(zhuǎn)布局
12)、判斷得到的visibleRange長(zhǎng)度,有效的話遍歷text中的attributes,配置對(duì)應(yīng)的layout屬性
13)、配置layout中的attachments
14)、配置結(jié)束,返回layout
繪制時(shí)就是根據(jù)layout中的配置情況繪制相應(yīng)的特征,這段代碼在此就不做分析了。
6.YYAsyncLayer文件
YYAsyncLayerDispalyTask是在YYAsyncLayer去background queue渲染是調(diào)用的對(duì)象,它有三個(gè)回調(diào),一個(gè)willDisplay在渲染之前、一個(gè)didDisplay在渲染之后和渲染時(shí)被調(diào)用的display。
YYAsyncLayer是CAlayer的子類,當(dāng)這個(gè)layer更新contents時(shí)就會(huì)調(diào)用delegate方法去調(diào)用async display task去background queue 渲染。這個(gè)delegate方法是YYAsyncLayerDelegate的方法。
YYAsyncLayer在刷新時(shí)調(diào)用_displayAsync:方法,然后調(diào)用遵守YYAsyncLayerDelegate的對(duì)象實(shí)現(xiàn)的newAsyncDisplayTask方法,獲取到需要繪制的前后和繪制時(shí)的task,根據(jù)是夠需要異步來(lái)判斷直接在主線程執(zhí)行繪制代碼還是異步執(zhí)行繪制代碼。
在異步繪制過(guò)程中用到了一個(gè)異步隊(duì)列,獲取方法是YYAsyncLayerGetDisplayQueue,在這個(gè)方法中有一個(gè)關(guān)于QOS的概念,NSQualityOfService(QOS) ios8之后提供的新功能,這個(gè)枚舉值是要告訴操作系統(tǒng)我們?cè)谶M(jìn)行什么樣的工作,讓系統(tǒng)能通過(guò)合理的資源控制來(lái)最高效的執(zhí)行任務(wù)代碼,主要涉及CPU調(diào)度、IO優(yōu)先級(jí)、任務(wù)運(yùn)行在哪個(gè)線程以及運(yùn)行的順序等等。
枚舉值的含義如下:
NSQualityOfServiceUserInteractive 與用戶交互的任務(wù),這些任務(wù)通常跟UI級(jí)別的刷新相關(guān),比如動(dòng)畫,這些任務(wù)需要在一瞬間完成
NSQualityOfServiceUserInitiated 由用戶發(fā)起的并且需要立即得到結(jié)果的任務(wù),比如滑動(dòng)scroll view時(shí)去加載數(shù)據(jù)用于后續(xù)cell的顯示,這些任務(wù)通常跟后續(xù)的用戶交互相關(guān),在幾秒或者更短的時(shí)間內(nèi)完成
NSQualityOfServiceUtility 一些可能需要花點(diǎn)時(shí)間的任務(wù),這些任務(wù)不需要馬上返回結(jié)果,比如下載的任務(wù),這些任務(wù)可能花費(fèi)幾秒或者幾分鐘的時(shí)間
NSQualityOfServiceBackground 這些任務(wù)對(duì)用戶不可見(jiàn),比如后臺(tái)進(jìn)行備份的操作,這些任務(wù)可能需要較長(zhǎng)的時(shí)間,幾分鐘甚至幾個(gè)小時(shí)
NSQualityOfServiceDefault 優(yōu)先級(jí)介于user-initiated 和 utility,當(dāng)沒(méi)有 QoS信息時(shí)默認(rèn)使用,開(kāi)發(fā)者不應(yīng)該使用這個(gè)值來(lái)設(shè)置自己的任務(wù)
Qos和GCD queue的對(duì)照?qǐng)D:

收獲到的小知識(shí)點(diǎn):
- iOS7 and later,
UIFont和CTFontRef是toll-free bridged的,在iOS6,UIFont是對(duì)CTFontRref的封裝,所以在CoreText中是可以使用UIFont的,但是在UILabel和UITextView中不能使用CTFontRef。 -
NSParagraphStype不是toll-free bridged 到CTParagraphStypeRef,CoreText可以同時(shí)使用兩者,但UILabel和UITextView只能使用NSParagraphStyle。 - 查看
.a靜態(tài)文件支持哪種iOS處理器




