本文只是讓自己加深下理解,方便以后查看,原作者看到有不合適的地方,或者大神們看到有不對(duì)的地方希望指正 ~
原文地址,尊重原作者 ~
<br />
一. 特點(diǎn)和用法
關(guān)于YYText的特點(diǎn)和用法請(qǐng)看@ibireme大神的github
二. 使用到的組件
介紹YYLabel之前先說一下
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
多了一些功能缀蹄,比如說可以使用blendMode
(圖形混合模式)、可以在shadow上再加一層shadow膘婶。關(guān)于blendMode
的學(xué)習(xí)缺前,可以參見喵神的博客YYTextDecoration:實(shí)現(xiàn)下劃線(underline)和中間截線(strikethrough)時(shí)使用,線條的形式給出了幾種樣式悬襟,可以通過YYTextLineStyle枚舉查看衅码。具體是
underline
還是strikethrough
是在NSAttributedString+YYText中NSMutableAttributeString(YYText)
中實(shí)現(xiàn)的方法。YYTextBorder:實(shí)現(xiàn)在文本周圍畫一個(gè)border脊岳,也可以是填充一個(gè)背景色逝段。
YYTextAttachment:封裝需要放入
text
中的對(duì)象。在說明文檔中提到割捅,如果attachment
是UIImage
奶躯,就繪制到CGContext
,如果是UIView或者CALayer就加入到text container
的view或者layer中亿驾。YYTextHightlight:當(dāng)
YYLabel
或者YYTextView
中的text可以被用戶按下時(shí)嘹黔,被按下的text
會(huì)有一個(gè)highlighted
狀態(tài),這時(shí)候就需要是用YYTextHighlight
來修改原來的text
莫瞬。所以這個(gè)對(duì)象和YYText
一樣儡蔓,只是是在highlight
狀態(tài)下的YYText
,而且添加了點(diǎn)擊和長(zhǎng)按事件疼邀。再來說一下NSAttributedString+YYText文件
在這個(gè)分類中主要是實(shí)現(xiàn)了幾類的操作:一些操作當(dāng)前
attributed string
的方法
比如說歸檔和反歸檔當(dāng)前字符串喂江、獲得某個(gè)位置的attributes
、字間距檩小、色值开呐、背景色烟勋、shadow等等。具體的參見文件筐付,基本上是作者封裝的方便獲得各種數(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
說明修改了這個(gè)text
奸焙。
作者簡(jiǎn)單的實(shí)現(xiàn)了MarkdownParser
和EmotionParser
瞎暑,兩個(gè)原理都差不多一樣,在這里只對(duì)EmotionParser
做一下簡(jiǎn)單的介紹与帆,希望能有所啟發(fā):
這段代碼(上圖)就是生成正則表達(dá)式_regex
和映射的字典_mapper
,第一層for是獲得你要匹配的key
了赌,第二層是如果有這些特殊的字符需要轉(zhuǎn)譯一下,然后將這些需要比配的key
用“|”連接起來玄糟。
這個(gè)(上圖)就是修改text
之后會(huì)被調(diào)用的方法勿她,在這個(gè)方法里對(duì)輸入的text
進(jìn)行匹配,如果匹配到之前_mapper
中需要替換的字符阵翎,就將這個(gè)字符串替換為需要替換的表情符逢并。
替換成表情符之后就需要重新計(jì)算這個(gè)表情符所占的range
了,這個(gè)方法就是拿到替換之后的的range
贮喧。
5.YYTextLayout
先看一下文檔中的說明筒狠,如下圖:
是不是很眼熟?好像在哪見過箱沦?是的辩恼,就是NSLayoutManager和NSTextContainer。他們的作用都是相似的谓形。
-
YYTextContainer
支持矩形(CGSize)
和圖形(UIBezierPath)
來初始化YYTextContainer;
在這里重點(diǎn)說一下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)行字形的渲染址遇。一般來說是一條橫線。
基礎(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、通過一些類方法初始化的方法(YYTextContainer
漾抬、CGSize
和text
)
2宿亡、layout
之后的attributes
,都是只讀的
3纳令、從layout
中讀取信息(位置挽荠、range
等等)
4克胳、繪制text layout
這個(gè)類主要是使用上面講過的所有的數(shù)據(jù)來繪制text
,這部分的代碼還是需要一點(diǎn)一點(diǎn)的去讀的圈匆,如果是新手每一行都會(huì)有收獲(比如說我)漠另,如果是老司機(jī)就沒有必要一行行的讀了,了解他的解題思路和解決這個(gè)問題的辦法就可以臭脓。下面說一下生成layout
的那個(gè)500行代碼的情況酗钞,就按照代碼的順序從上往下大概的說明一下干了什么。
* 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
就沒有用了)
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ù)是夠需要異步來判斷直接在主線程執(zhí)行繪制代碼還是異步執(zhí)行繪制代碼章贞。
在異步繪制過程中用到了一個(gè)異步隊(duì)列,獲取方法是YYAsyncLayerGetDisplayQueue
非洲,在這個(gè)方法中有一個(gè)關(guān)于QOS的概念鸭限,NSQualityOfService(QOS)
ios8之后提供的新功能,這個(gè)枚舉值是要告訴操作系統(tǒng)我們?cè)谶M(jìn)行什么樣的工作两踏,讓系統(tǒng)能通過合理的資源控制來最高效的執(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ì)用戶不可見珠增,比如后臺(tái)進(jìn)行備份的操作超歌,這些任務(wù)可能需要較長(zhǎng)的時(shí)間,幾分鐘甚至幾個(gè)小時(shí)
NSQualityOfServiceDefault 優(yōu)先級(jí)介于user-initiated 和 utility蒂教,當(dāng)沒有 QoS信息時(shí)默認(rèn)使用巍举,開發(fā)者不應(yīng)該使用這個(gè)值來設(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
中不能使用CTFontRe
f炭分。 -
NSParagraphStype
不是toll-free bridged 到CTParagraphStypeRef
桃焕,CoreText
可以同時(shí)使用兩者,但UILabel
和UITextView
只能使用NSParagraphStyle
捧毛。 - 查看
.a
靜態(tài)文件支持哪種iOS處理器