CALayer繪制流程

關(guān)系

UIView是如何顯示一個(gè)頁(yè)面的贵扰?

  1. CALayer通過(guò)代理,將需要繪制的信息傳遞給UIView浇雹;
  2. UIView通過(guò)CoreGraphics進(jìn)行繪制的操作阁谆;
  3. 繪制好的內(nèi)容交給CALayer,方法有兩種:
  • layer.contents = image
  • 存儲(chǔ)在CABackingStore
  1. 通過(guò)OpenGL ES/Metal(GPU)進(jìn)行顯示晾腔。
  • CALayer的調(diào)用流程

CALayer調(diào)用流程,分為布局繪制兩部分啊犬。

  1. 布局部分灼擂,先會(huì)調(diào)用CALayerlayerSublayers方法,再調(diào)用viewlayoutSubViews觉至;
  2. 繪制部分剔应,通過(guò)設(shè)置layersetNeedsDisplay標(biāo)記需要刷新,然后調(diào)用CALayerdisplay方法,再判斷UIViewdisplayLayer方法是否實(shí)現(xiàn)峻贮,如果實(shí)現(xiàn)走自定義繪制流程席怪,否則進(jìn)入系統(tǒng)繪制流程,會(huì)先創(chuàng)建buffercontext纤控,然后調(diào)用drawLayer:InContext:挂捻,再執(zhí)行UIViewdrawRect
流程
  1. 通過(guò)在drawRect進(jìn)行斷點(diǎn)船万,查看函數(shù)調(diào)用棧:
 * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
 * frame #0: 0x0000000103de7317 CALayerProcessDemo`-[LCustomLayerView drawRect:](self=0x00007ff154508f80, _cmd="drawRect:", rect=(origin = (x = 0, y = 0), size = (width = 200, height = 200))) at LCustomLayerView.m:13:5
 frame #1: 0x00000001083704d3 UIKitCore`-[UIView(CALayerDelegate) drawLayer:inContext:] + 550  //默認(rèn)調(diào)取
 frame #2: 0x00000001098f393d QuartzCore`-[CALayer drawInContext:] + 285
 frame #3: 0x00000001097ee969 QuartzCore`CABackingStoreUpdate_ + 173 //后臺(tái)存儲(chǔ)區(qū)
 frame #4: 0x00000001098fa3fc QuartzCore`___ZN2CA5Layer8display_Ev_block_invoke + 44
 frame #5: 0x00000001098f3439 QuartzCore`-[CALayer _display] + 2443  //繪制
 frame #6: 0x0000000109904e77 QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 359
 frame #7: 0x000000010987424a QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 328
 frame #8: 0x00000001098ab606 QuartzCore`CA::Transaction::commit() + 610
 frame #9: 0x0000000107eab2c3 UIKitCore`__34-[UIApplication _firstCommitBlock]_block_invoke_2 + 128
 frame #10: 0x0000000105081cbc CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
 frame #11: 0x0000000105081480 CoreFoundation`__CFRunLoopDoBlocks + 336
 frame #12: 0x000000010507bd04 CoreFoundation`__CFRunLoopRun + 1252
 frame #13: 0x000000010507b4d2 CoreFoundation`CFRunLoopRunSpecific + 626
 frame #14: 0x000000010d6bf2fe GraphicsServices`GSEventRunModal + 65
 frame #15: 0x0000000107e91fc2 UIKitCore`UIApplicationMain + 140
 frame #16: 0x0000000103de76a0 CALayerProcessDemo`main(argc=1, argv=0x00007ffeebe17ea8) at main.m:14:16
 frame #17: 0x0000000106a00541 libdyld.dylib`start + 1
 frame #18: 0x0000000106a00541 libdyld.dylib`start + 1
  1. 從調(diào)用棧中可以看到在調(diào)用drawRect之前刻撒,還會(huì)調(diào)用-[CALayer _display](視圖繪制)、-[CALayer drawInContext:](默認(rèn)調(diào)用)耿导、-[UIView(CALayerDelegate) drawLayer:inContext:](自定義繪制流程)等方法声怔,我們?cè)囍鴮?shí)現(xiàn)drawLayer:inContext時(shí),發(fā)現(xiàn)drawRect不會(huì)再走舱呻。

下面我們通過(guò)自定義CALayer醋火,來(lái)分析下系統(tǒng)的CALayer到底做了什么:

@protocol LLayerDelegate <NSObject>

- (void)l_layoutSublayersOfLayer:(CALayer *)layer;
- (void)l_displayLayer:(CALayer *)layer;
- (void)l_drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;

- (CGContextRef)l_beginDraw;
- (void)l_endDraw;

@end
@interface LLayer : CALayer

@end
@implementation LLayer
- (void)layoutSublayers { //這個(gè)會(huì)調(diào)用UIView layoutSubviews
    __strong id <LLayerDelegate> delegate = (id<LLayerDelegate>)self.delegate;
    if ([delegate respondsToSelector:@selector(l_layoutSublayersOfLayer:)]) {
        //調(diào)用代理的實(shí)現(xiàn)
    }
}

- (void)display {
    //詢(xún)問(wèn)代理繪制
    __strong id <LLayerDelegate> delegate = (id<LLayerDelegate>)self.delegate;
    CGContextRef context = [delegate l_beginDraw];
    //如何繪制
    [delegate l_drawLayer:self inContext:context];
        
    [delegate l_displayLayer:self];
        
    [delegate l_endDraw];
}
@end
@interface LCustomLayerView()<LLayerDelegate>
@end

@implementation LCustomLayerView
//下面我們模擬下layer的執(zhí)行流程
//layer走自定義的
+ (Class)layerClass {
    return [LLayer class];
}

#pragma -mark LLayerDelegate
- (void)l_layoutSublayersOfLayer:(CALayer *)layer {
    [self layoutSubviews];
}

- (CGContextRef)l_beginDraw {
    //開(kāi)始繪制,并返回context
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.layer.opaque, self.layer.contentsScale);
    CGContextRef cRef = UIGraphicsGetCurrentContext();
    return cRef;
}

- (void)l_drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
    [[UIColor blueColor] set];
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(self.bounds.size.width/2.0 - 30, self.bounds.size.height/2.0 - 30, 60, 60)];
    CGContextAddPath(ctx, path.CGPath);
    CGContextFillPath(ctx);
}

//自定義繪制
- (void)l_displayLayer:(CALayer *)layer {
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    dispatch_async(dispatch_get_main_queue(), ^{
        layer.contents = (__bridge id _Nullable)(img.CGImage);
    });
}

- (void)l_endDraw {
    UIGraphicsEndImageContext();
}

@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    [self.view addSubview:self.clView];
}
- (LCustomLayerView *)clView {
    if (!_clView) {
        _clView = [LCustomLayerView new];
        _clView.frame = CGRectMake(50, 100, 200, 200);
        _clView.backgroundColor = [UIColor orangeColor];
    }
    return _clView;
}

@end
  • 補(bǔ)充
  1. 重新布局方法箱吕,layoutSubviews調(diào)用時(shí)機(jī):
  • 延時(shí)執(zhí)行
    • 被添加到父視圖中
    • 添加子控件
    • 尺寸發(fā)生變化
    • 子控件尺寸發(fā)生變化
    • 滾動(dòng)UIScrollView
    • 旋轉(zhuǎn)屏幕
    • setNeedsLayout芥驳,且有布局需要更新
  • 立即執(zhí)行
    • 調(diào)用layoutIfNeeded時(shí)且有變化會(huì)立即觸發(fā)
  1. 重新繪制方法,drawRect調(diào)用時(shí)機(jī)
  • loadView殖氏,viewDidLoad方法之后調(diào)用
  • View初始化時(shí)沒(méi)有設(shè)置rect大小晚树,將直接導(dǎo)致drawRect不會(huì)被調(diào)用
  • 在調(diào)用sizeThatFits后被調(diào)用,可以先調(diào)用sizeToFit計(jì)算出size雅采,然后系統(tǒng)自動(dòng)調(diào)用drawRect
  • 通過(guò)設(shè)置contentModeUIViewContentModeRedraw爵憎,每次設(shè)置或更改frame的時(shí)候自動(dòng)調(diào)用drawRect
  • rect不為0,調(diào)用setNeedsDisplaysetNeedsDisplayInRect會(huì)觸發(fā)

{\large\text{作者:lmfei 鏈接:http://www.reibang.com/p/f05b8f1d27f2 來(lái)源:簡(jiǎn)書(shū) 著作權(quán)歸作者所有婚瓜。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)宝鼓,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。}}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末巴刻,一起剝皮案震驚了整個(gè)濱河市愚铡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胡陪,老刑警劉巖沥寥,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異柠座,居然都是意外死亡邑雅,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)妈经,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)淮野,“玉大人捧书,你說(shuō)我怎么就攤上這事≈栊牵” “怎么了经瓷?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)洞难。 經(jīng)常有香客問(wèn)我舆吮,道長(zhǎng),這世上最難降的妖魔是什么廊营? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任歪泳,我火速辦了婚禮,結(jié)果婚禮上露筒,老公的妹妹穿的比我還像新娘呐伞。我一直安慰自己,他們只是感情好慎式,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布伶氢。 她就那樣靜靜地躺著,像睡著了一般瘪吏。 火紅的嫁衣襯著肌膚如雪癣防。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天掌眠,我揣著相機(jī)與錄音蕾盯,去河邊找鬼。 笑死蓝丙,一個(gè)胖子當(dāng)著我的面吹牛级遭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播渺尘,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼挫鸽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了鸥跟?” 一聲冷哼從身側(cè)響起丢郊,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎医咨,沒(méi)想到半個(gè)月后枫匾,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拟淮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年干茉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惩歉。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡等脂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出撑蚌,到底是詐尸還是另有隱情上遥,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布争涌,位于F島的核電站粉楚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏亮垫。R本人自食惡果不足惜模软,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望饮潦。 院中可真熱鬧燃异,春花似錦、人聲如沸继蜡。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)稀并。三九已至仅颇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間碘举,已是汗流浹背忘瓦。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留引颈,地道東北人耕皮。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像线欲,于是被迫代替她去往敵國(guó)和親明场。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • UIView是如何顯示一個(gè)頁(yè)面的李丰? CALayer通過(guò)代理苦锨,將需要繪制的信息傳遞給View UIView通過(guò)Cor...
    lmfei閱讀 695評(píng)論 0 0
  • 視圖渲染框架 UIKit是常用的框架,顯示趴泌、動(dòng)畫(huà)都通過(guò)CoreAnimation舟舒。CoreAnimation是核心...
    人生看淡不服就干閱讀 4,245評(píng)論 0 9
  • 前言 ? iOS開(kāi)發(fā)中 UI是很重要也是最直觀可見(jiàn)的一部分秃励,而所有的控件都是繼承自UIView的,UIView...
    就叫yang閱讀 2,519評(píng)論 1 8
  • 1 UIView和CALayer關(guān)系 UIView是iOS系統(tǒng)中界面元素的基礎(chǔ)吉捶,所有的界面元素都是繼承自它夺鲜。它真正...
    Claire_wu閱讀 1,363評(píng)論 0 1
  • iOS的渲染和繪制機(jī)制 顯示器原理和技術(shù) 電子槍逐行掃描(HSync)皆尔,一幀畫(huà)面繪制完成后,復(fù)原準(zhǔn)備下一幀(VSy...
    六橫六豎亞閱讀 1,747評(píng)論 0 0