屏幕為什么會(huì)卡頓? 通常的我們熟悉的
drawRect:
方法,但是是什么時(shí)候調(diào)用的? 一個(gè)視圖是怎么樣在手機(jī)上顯示的? CALayer是怎么樣異步繪制? 帶著這些問題我們往下研究一下聂喇。
一: 視圖顯示
我們的視圖要想顯示在手機(jī)上暴匠,一般的需要通過CPU 和 GPU的處理,IOS屏幕的
Vsyn
信號(hào)的頻率是1/60s
,如果在這個(gè)時(shí)間內(nèi)還沒處理完,就會(huì)導(dǎo)致卡頓房维。
CPU渲染
- Layout: UI的Frame 計(jì)算
- 繪制: display階段,就是我們通過
Core Graphics
來處理 - prepare: 如果是圖片的話沼瘫,需要進(jìn)行圖片解碼
- commit: 其實(shí)通過CoreAnimation提交給OpenGL,layer
GPU渲染
頂點(diǎn)著色咙俩、圖元裝配耿戚、片段著色湿故、片段處理 大概有個(gè)概念就行,我們主要討論
FrameBuffer
和光柵化
, 因?yàn)檫@涉及到離屏幕渲染
膜蛔、在屏幕渲染
坛猪、圖層混合合成
-
在屏幕渲染
:表示在當(dāng)前的屏幕的緩存區(qū)內(nèi)渲染 -
離屏幕渲染
:其實(shí)是在未預(yù)合成之前
不能在當(dāng)前屏幕渲染,需要放到當(dāng)前屏幕緩沖區(qū)外緩存皂股;我們可以通過開啟Color Offscreen-Rendered Yellow
來檢查墅茉。主要是以下事件會(huì)導(dǎo)致離屏幕渲染:1. 設(shè)置圓角 + maskToBounds 2. 圖層蒙版 3. 陰影、漸變呜呐、不透明就斤、抗鋸齒(edge antialiasing) 4. 光柵化: (1.) 將圖轉(zhuǎn)換為 用柵格 組成的bitmap,每個(gè)格柵對應(yīng)幀緩沖的一個(gè)位置,這樣下次如果圖片沒有改變的話就可以直接復(fù)用蘑辑。 (2.) 通過 “Color Hits Green and Misses Red”,來檢測洋机,綠色表示復(fù)用。 (3.) 通常的我們會(huì)選擇 有多種層級的視圖并且含有透明通道洋魂, 可以進(jìn)行 光柵化,減少渲染時(shí)間 (4.) 開啟: layer.shouldRasterize=YES & layer.contentsScale = [UIScreen mainScreen].scale
二: 繪制
IOS 通過UIView 來處理事件的傳遞問題绷旗,而繪制是通過CALayer來處理,其實(shí)就是很好的
單一模式
,同時(shí)的calayer.delegate = uiview
忧设。在UIView 和 CALayer 的原碼方面我主要是參考Chameleon
1. CALayer 繪制過程
CALayer 通過
-(void)display
方法來進(jìn)行繪制刁标,這個(gè)時(shí)候會(huì)判斷delegate 是否實(shí)現(xiàn)了-(void)displayLayer:
,如果實(shí)現(xiàn)了就進(jìn)行自定義繪制流程,反之則進(jìn)行 CALayer系統(tǒng)的繪制來自定義創(chuàng)建buffer 和 context址晕,創(chuàng)建好了膀懈,就判斷是否有代理實(shí)現(xiàn)了drawLayer:inContext:
這樣可以把context 傳遞過去。
2. 異步繪制
就是通過 實(shí)現(xiàn) CAlayer代理的 -(void)displayLayer:
谨垃,自己創(chuàng)建 buffer 和 context 启搂,繪制結(jié)束后脾拆,通過commit
的時(shí)候進(jìn)行提交給GPU處理围来。
一般的有下面
// 創(chuàng)建 context 和 進(jìn)行繪制枷踏, 一般是放在子線程處理
CGBitmapContextCreate()
....CoreGraphic API
id content = CGBitmapContextCreateImage()
//進(jìn)行提交操作荣月,需要回到主線程進(jìn)行處理
[CALayer setContents:]
3. Chameleon
源碼,查看UIView
通過查看Chameleon
, UIView實(shí)現(xiàn)了 drawLayer:inContext:
方法.可以清楚的看到 通過創(chuàng)建好的
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
UIGraphicsPushContext(ctx);
UIGraphicsPushContext(ctx);
const CGRect bounds = CGContextGetClipBoundingBox(ctx);
[self.backgroundColor setFill]; //背景色的設(shè)置
CGContextFillRect(ctx,bounds);
'[self drawRect:bounds];' //添加自定義的繪制
CGContextRestoreGState(ctx);
UIGraphicsPopContext();
}