YYKit之YYText


原文:http://www.cnblogs.com/lujianwenance/p/5716804.html
 
本文的目的是希望能幫助到我們更快的熟悉和學習YYText的結構和實現的思路,如有不正確或者不准確的地方請指正,謝謝。  
一、特點與用法
     關於YYText的特點和用法請看@ibireme大神的github:
二、使用到的組件
     1、介紹YYLabel之前先說一下YYTextAttribute,因為后面會大量的使用到它。
     YYTextAttribute:
     1)定義的一些Enum,YYTextAttributeType:attribute的類型,有None、UIKit、CoreText和YYText四種類型;YYTextLineStyle:line的樣式;YYTextVerticalAlignment:垂直方向text的位置;YYTextDirection:text的位置;YYTextTruncationType:text截斷的位置。
     2)YYText中定義的Attribute Name。主要是獨有的一些類型。
     3)YYTextBackedString:可以將一些表情圖片映射成純文本。
     4)YYTextBinding:使一些特定的字符串綁定在一起,YYTextView在選擇和編輯他們的時候把他們當成一個單獨的字符。
     5)YYTextShadow:用處和NSShadow一樣,只是比NSShadow多了一些功能,比如說可以使用blendMode(圖形混合模式)、可以在shadow上再加一層shadow。關於blendMode的學習,可以參見喵神的博客: https://onevcat.com/2013/04/using-blending-in-ios/
     6)YYTextDecoration:實現下划線(underline)和中間截線(strikethrough)時使用,線條的形式給出了幾種樣式,可以通過YYTextLineStyle枚舉查看。具體是underline還是strikethrough是在NSAttributedString+YYText中NSMutableAttributeString(YYText)中實現的方法。
     7)YYTextBorder:實現在文本周圍畫一個border,也可以是填充一個背景色。
     8)YYTextAttachment:封裝需要放入text中的對象。在說明文檔中提到,如果attachment是UIImage,就繪制到CGContext,如果是UIView或者CALayer就加入到text container的view或者layer中。
     9)YYTextHightlight:當YYLabel或者YYTextView中的text可以被用戶按下時,被按下的text會有一個highlighted 狀態,這時候就需要是用YYTextHighlight來修改原來的text。所以這個對象和YYText一樣,只是是在highlight狀態下的YYText,而且添加了點擊和長按事件。
     2、再來說一下NSAttributedString+YYText文件
       在這個分類中主要是實現了幾類的操作:
          1)一些操作當前attributed string的方法
               比如說歸檔和反歸檔當前字符串、獲得某個位置的attributes、字間距、色值、背景色、shadow等等。具體的參見文件,基本上是作者封裝的方便獲得各種數據的方法。(和Foundation中比強大太多了)。
          2)為YYText創建attachment的方法
          3)為YYText添加YYText特有的attribute的方法
          4)添加像設置屬性一樣的設置character attribute的font、color、backgroundColor等等的方法。
          5)添加像設置屬性一樣的設置paragraph attribute的方法。
          6)添加像設置屬性一樣的設置YYText attribute的方法。 
          7)使用range設置不連續的attribute的方法
          8)設置text highlight 的便捷方法
          9)和其他的工具型的方法。
             
     3、NSParagraphStyle+YYText文件
          提供了CoreText中的CTParagraphStyleRef和NSParagraphStyle之間的轉化。
     4、YYTextParser
          這是一個protocol,聲明了一個-(BOOL)parseText:(NSMutableAttributedString *)string selectedRange:(NSRangePointer)selectedRange;方法。這個方式是遵守這個協議必須實現的方法,當YYTextView或者YYLabel中的text改變時被調用。返回YES說明修改了這個text。
          作者簡單的實現了MarkdownParser和EmotionParser,兩個原理都差不多一樣,在這里只對EmotionParser做一下簡單的介紹,希望能有所啟發:
     這段代碼就是生成正則表達式_regex和映射的字典_mapper,第一層for是獲得你要匹配的key,第二層是如果有這些特殊的字符需要轉譯一下,然后將這些需要比配的key 用“|”連接起來。
這個就是修改text之后會被調用的方法,在這個方法里對輸入的text進行匹配,如果匹配到之前_mapper中需要替換的字符,就將這個字符串替換為需要替換的表情符。
替換成表情符之后就需要重新計算這個表情符所占的range了,這個方法就是拿到替換之后的的range。
          5、YYTextLayout
               先看一下文檔中的說明,如下圖:
是不是很眼熟?好像在哪見過?是的,就是NSLayoutManager和NSTextContainer。他們的作用都是相似的。
          1)YYTextContainer
               支持矩形(CGSize)和圖形(UIBezierPath)來初始化YYTextContainer;
               在這里重點說一下YYTextLinePositionModifier,它是 一個協議,定義了一個必須實現的方法,這個方法將會在layout完成的時候被調用,三個參數分別是存放YYTextLine的數組、完整的text和layout container。
               YYTextLine:它是封裝了CTLineRef的對象,封裝了每一行text的具體展示位置、range、這一行擁有的attachments等等,只有一個類方法的初始化方法。如果不了解一些自行描述集的內容,對textLine中的一些屬性和操作會不是很清晰,看下圖:
     
     
邊框(Bounding Box):一個假想的邊框,盡可能地容納整個字形。
基線(Baseline):一條假想的參照線,以此為基礎進行字形的渲染。一般來說是一條橫線。
基礎原點(Origin):基線上最左側的點。
行間距(Leading):行與行之間的間距。
字間距(Kerning):字與字之間的距離,為了排版的美觀,並不是所有的字形之間的距離都是一致的,但是這個基本步影響到我們的文字排版。
上行高度(Ascent)和下行高度(Decent):一個字形最高點和最低點到基線的距離,所以行高就是ascent + decent。
     看完上面的簡單介紹你就能明白,在YYTextLine的setCTLine中的代碼邏輯是從CTLineRef中取出對應的行寬、上行高度、下行高度、行間距、rangge和第一個字型符的位置(這個在垂直布局會用到)。之后調用reloadBounds方法,重新計算當前行的bounds、attachments所在的range和rect。
          2)YYTextLayout
               這個真的是核心內容了,這個文件一共3300多行的代碼,從代碼量上就能看出它的地位。這個類中存儲着text的layout結果,所有的property都是readonly的。實現的接口有:
               1、通過一些類方法初始化的方法(YYTextContainer、CGSize和text)
               2、layout之后的attributes,都是只讀的
               3、從layout中讀取信息(位置、range等等)
               4、繪制text layout
               這個類主要是使用上面講過的所有的數據來繪制text,這部分的代碼還是需要一點一點的去讀的,如果是新手每一行都會有收獲(比如說我),如果是老司機就沒有必要一行行的讀了,了解他的解題思路和解決這個問題的辦法就可以。下面說一下生成layout的那個500行代碼的情況,就按照代碼的順序從上往下大概的說明一下干了什么。
                         1)、初始化一系列使用到的變量
                         2)、安全判斷,text和container
                         3)、判斷是否需要修復emoji的bug(iOS8.3中)
                         4)、判斷是否設置了path屬性和exclusionPaths數組,做相應的計算拿到cgpath,如果cgpath為空則goto fail 返回nil(如果設置了path,size和insets就沒有用了)
                         5)、判斷是不是奇偶填充,判斷pathWidth是否為0,判斷是否是垂直展示
                         6)、使用text創建CTFramesetterRef,創建失敗goto fail
                         7)、使用上一步中創建的frameSetter創建CTFrameRef
                         8)、從CTFrameRef的對象中獲得每一行、ctRun數組,計算每一行的frame,判斷是否實現了linePositionModifier這個協議,有的話調用協議方法。
                         9)、計算bounding size
                         10)、判斷是否需要truncation,和按那種方式處理
                         11)、判斷是否垂直布局text,需要的話,旋轉布局
                         12)、判斷得到的visibleRange長度,有效的話遍歷text中的attributes,配置對應的layout屬性
                         13)、配置layout中的attachments
                         14)、配置結束,返回layout
                    繪制時就是根據layout中的配置情況繪制相應的特征,這段代碼在此就不做分析了。
          6、YYAsyncLayer文件
               YYAsyncLayerDispalyTask是在YYAsyncLayer去background queue 渲染是調用的對象,它有三個回調,一個willDisplay在渲染之前、一個didDisplay在渲染之后和渲染時被調用的display。
               YYAsyncLayer是CAlayer的子類,當這個layer更新contents時就會調用delegate方法去調用async display task 去background queue 渲染。這個delegate方法是YYAsyncLayerDelegate的方法。
               YYAsyncLayer在刷新時調用_displayAsync:方法,然后調用遵守YYAsyncLayerDelegate的對象實現的newAsyncDisplayTask方法,獲取到需要繪制的前后和繪制時的task,根據是夠需要異步來判斷直接在主線程執行繪制代碼還是異步執行繪制代碼。
               在異步繪制過程中用到了一個異步隊列,獲取方法是YYAsyncLayerGetDisplayQueue,在這個方法中有一個關於QOS的概念,NSQualityOfService(QOS)ios8之后提供的新功能,這個枚舉值是要告訴操作系統我們在進行什么樣的工作,讓系統能通過合理的資源控制來最高效的執行任務代碼,主要涉及CPU調度、IO優先級、任務運行在哪個線程以及運行的順序等等。
      枚舉值的含義如下:
            NSQualityOfServiceUserInteractive
            與用戶交互的任務,這些任務通常跟UI級別的刷新相關,比如動畫,這些任務需要在一瞬間完成
            NSQualityOfServiceUserInitiated
            由用戶發起的並且需要立即得到結果的任務,比如滑動scroll view時去加載數據用於后續cell的顯示,這些任務通常跟后續的用戶交互相關,在幾秒或者更短的時間內完成
            NSQualityOfServiceUtility
             一些可能需要花點時間的任務,這些任務不需要馬上返回結果,比如下載的任務,這些任務可能花費幾秒或者幾分鍾的時間

            NSQualityOfServiceBackground
            這些任務對用戶不可見,比如后台進行備份的操作,這些任務可能需要較長的時間,幾分鍾甚至幾個小時

            NSQualityOfServiceDefault

            優先級介於user-initiated 和 utility,當沒有 QoS信息時默認使用,開發者不應該使用這個值來設置自己的任務

     
 Qos和GCD queue的對照圖:

 

     看詳細的分析請到這里: http://www.jianshu.com/p/f9e01c69a46f
     收獲到的小知識點:
     1、iOS7 and later,UIFont 和CTFontRef是 toll-free bridged的,在iOS6,UIFont是對CTFontRref的封裝,所以在CoreText中是可以使用UIFont的,但是在UILabel和UITextView中不能使用CTFontRef。
     2、NSParagraphStype不是toll-free bridged 到CTParagraphStypeRef,CoreText可以同時使用兩者,但UILabel和UITextView只能使用NSParagraphStyle。
     3、查看.a靜態文件支持哪種iOS處理器
     
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM