UI繪制的過(guò)程

當(dāng)調(diào)用[UIView setNeedsDisplay]方法時(shí),系統(tǒng)會(huì)立即調(diào)用對(duì)應(yīng)的layer的setNeedsDisplay,之后給layer打上標(biāo)記,在一次RunLoop將要結(jié)束時(shí),會(huì)調(diào)用[CALayer display]方法,然后進(jìn)入到真正的繪制過(guò)程當(dāng)中。
[CALayer display]方法內(nèi)部實(shí)現(xiàn)中,會(huì)判斷有沒(méi)有l(wèi)ayer的delegate響應(yīng)了displayLayer方法,如果沒(méi)有會(huì)執(zhí)行系統(tǒng)繪制流程,如果響應(yīng)了就為我們提供了異步繪制的入口。
系統(tǒng)繪制流程
在CALayer內(nèi)部會(huì)創(chuàng)建一個(gè)backing store(CGContextRef),然后layer會(huì)判斷它是否有代理,如果沒(méi)有代理的話,會(huì)調(diào)用[CALayer drawInContext:], 如果有代理,會(huì)調(diào)用[layer.delegate drawLayer: inContext],然后做當(dāng)前視圖的繪制工作,這部分是發(fā)生在系統(tǒng)內(nèi)部的,然后在一個(gè)合適的時(shí)機(jī)給予我們一個(gè)回調(diào)方法,就是[UIView drawRect:],[UIView drawRect:]的默認(rèn)實(shí)現(xiàn)是什么都不做,給我們開(kāi)一個(gè)口子,就允許我們?cè)谙到y(tǒng)繪制的基礎(chǔ)上做一些其他的相關(guān)的繪制工作,最后不論是哪個(gè)分支,都是由CALayer上傳對(duì)應(yīng)的backing store(可以理解為位圖)到GPU
異步繪制
[layer.delegate drawLayer:inContext:]方法實(shí)現(xiàn)就可以進(jìn)入到異步繪制的流程
- 代理負(fù)責(zé)生產(chǎn)對(duì)應(yīng)的bitmap
- 設(shè)置該bitmap作為layer.contents屬性的值
異步繪制的機(jī)制和流程

在調(diào)用setNeedsDisplay方法之后,在當(dāng)前RunLoop快要結(jié)束時(shí),由系統(tǒng)調(diào)用視圖所對(duì)應(yīng)的CALayer的display方法,然后如果代理實(shí)現(xiàn)了displayLayer:函數(shù),會(huì)調(diào)用代理的displayLayer:函數(shù)方法,然后會(huì)通過(guò)子線程的切換,在子線程中做位圖的繪制,此時(shí)主線程可以做別的事。
在全局并發(fā)隊(duì)列子線程中
- 通過(guò)
CGBitmapContextCreate()函數(shù)來(lái)創(chuàng)建一個(gè)位圖的上下文 - 通過(guò)CoreGraphics的相關(guān)api做當(dāng)前UI空間的繪制工作
- 再通過(guò)
CGBitmapContextCreateImage()函數(shù)來(lái)根據(jù)當(dāng)前所繪制的上下文,生成一張CGImage圖片 - 然后回到主隊(duì)列中提交位圖,設(shè)置給CALayer的contents屬性,這樣就完成了一個(gè)UI控件的異步繪制
來(lái)自 https://leejnull.github.io/2020/03/09/2020-03-09-01/