UI繪制的過程
當調(diào)用[UIView setNeedsDisplay]
方法時,系統(tǒng)會立即調(diào)用對應的layer的setNeedsDisplay
,之后給layer打上標記,在一次RunLoop將要結(jié)束時,會調(diào)用[CALayer display]
方法,然后進入到真正的繪制過程當中。
[CALayer display]
方法內(nèi)部實現(xiàn)中,會判斷有沒有l(wèi)ayer的delegate響應了displayLayer
方法,如果沒有會執(zhí)行系統(tǒng)繪制流程,如果響應了就為我們提供了異步繪制的入口。
系統(tǒng)繪制流程
在CALayer內(nèi)部會創(chuàng)建一個backing store(CGContextRef),然后layer會判斷它是否有代理,如果沒有代理的話,會調(diào)用[CALayer drawInContext:]
, 如果有代理,會調(diào)用[layer.delegate drawLayer: inContext]
,然后做當前視圖的繪制工作,這部分是發(fā)生在系統(tǒng)內(nèi)部的,然后在一個合適的時機給予我們一個回調(diào)方法,就是[UIView drawRect:]
,[UIView drawRect:]
的默認實現(xiàn)是什么都不做,給我們開一個口子,就允許我們在系統(tǒng)繪制的基礎上做一些其他的相關的繪制工作,最后不論是哪個分支,都是由CALayer上傳對應的backing store(可以理解為位圖)到GPU
異步繪制
[layer.delegate drawLayer:inContext:]
方法實現(xiàn)就可以進入到異步繪制的流程
- 代理負責生產(chǎn)對應的bitmap
- 設置該bitmap作為layer.contents屬性的值
異步繪制的機制和流程
在調(diào)用setNeedsDisplay
方法之后,在當前RunLoop快要結(jié)束時,由系統(tǒng)調(diào)用視圖所對應的CALayer的display方法,然后如果代理實現(xiàn)了displayLayer:
函數(shù),會調(diào)用代理的displayLayer:
函數(shù)方法,然后會通過子線程的切換,在子線程中做位圖的繪制,此時主線程可以做別的事。
在全局并發(fā)隊列子線程中
- 通過
CGBitmapContextCreate()
函數(shù)來創(chuàng)建一個位圖的上下文 - 通過CoreGraphics的相關api做當前UI空間的繪制工作
- 再通過
CGBitmapContextCreateImage()
函數(shù)來根據(jù)當前所繪制的上下文,生成一張CGImage圖片 - 然后回到主隊列中提交位圖,設置給CALayer的contents屬性,這樣就完成了一個UI控件的異步繪制