iOS UIView繪制&異步繪制

1. UIView的繪制流程圖

  1. UIView調(diào)用setNeedsDisplay,但是沒立即進(jìn)行視圖的繪制工作;
  2. UIView調(diào)用setNeedDisplay后,系統(tǒng)調(diào)用view對應(yīng)layersetNeedsDisplay方法;
  3. 當(dāng)前runloop即將結(jié)束的時候調(diào)用CALayerdisplay方法;
  4. runloop即將結(jié)束, 開始視圖的繪制流程;

知識點(diǎn):為什么調(diào)用UIViewsetNeedsDisplay后界面并沒有立即繪制?
在當(dāng)前Runloop將要結(jié)束的時候才會開始界面的繪制;


2. 系統(tǒng)繪制流程圖


1.CALayer內(nèi)部創(chuàng)建一個backing store(CGContextRef)();

  1. 判斷l(xiāng)ayer是否有代理;
    2.1 有代理:調(diào)用delegetedrawLayer:inContext, 然后在合適的實(shí)際回調(diào)代理, 在[UIView drawRect]中做一些繪制工作;
    2.2 沒有代理:調(diào)用layerdrawInContext方法,
  2. layer上傳backingStoreGPU, 結(jié)束系統(tǒng)的繪制流程;

3. 異步繪制

什么是異步繪制?
個人理解為:展示界面的過程中將創(chuàng)建上下文和控件的繪制工作放到子線程中, 子線程將那些工作完成渲染成圖片后轉(zhuǎn)回主線程然后將位圖展示在界面上;
異步繪制的入口在[layer.delegate displayLayer], 異步繪制過程中代理負(fù)責(zé)生成對應(yīng)的位圖(bitmap);然后將bitmap賦值給layer.content屬性展示;

關(guān)于異步繪制,可以參考下圖片的的解碼邏輯
例如 imgV.image = [UIImage imageNamed:@"1.png"];賦值左側(cè)后其實(shí)并不是直接展示到屏幕上的,而是需要壓縮圖片的二進(jìn)制數(shù)據(jù), 然后再解碼成屏幕可以識別的數(shù)據(jù)格式(位圖); 這個解碼過程默認(rèn)都是在主線程進(jìn)行的; 將其放在子線程則可以減輕主線程壓力; 這個過程可以參考SDWebImage中的+ (UIImage *)decodedImageWithImage:(UIImage *)image函數(shù)的處理邏輯, 每次圖片下載后是先在子線程將圖片解碼成位圖, 然后用的時候在主線程展示;

異步繪制流程
  1. 某個時機(jī)調(diào)用setNeedsDisplay;
  2. runloop將要結(jié)束的時候調(diào)用[CALayer display]
  3. 如果代理實(shí)現(xiàn)了dispalyLayer將會調(diào)用此方法, 在子線程中去做異步繪制的工作;
  4. 子線程中做的工作:創(chuàng)建上下文, 控件的繪制, 生成圖片;
  5. 轉(zhuǎn)到主線程, 設(shè)置layer.contents, 將生成的視圖展示在layer上面;

異步繪制示例代碼:

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN
@interface AsyncLabel : UIView
@property (nonatomic, copy)     NSString    *asynText;
@property (nonatomic, strong)   UIFont      *asynFont;
@property (nonatomic, strong)   UIColor     *asynBGColor;
@end
NS_ASSUME_NONNULL_END
#import "AsyncLabel.h"
#import <CoreText/CoreText.h>

@implementation AsyncLabel

- (void)displayLayer:(CALayer *)layer {
    /**
     除了在drawRect方法中, 其他地方獲取context需要自己創(chuàng)建[http://www.reibang.com/p/86f025f06d62]
     coreText用法簡介:[https://www.cnblogs.com/purple-sweet-pottoes/p/5109413.html]
     */
      CGSize size = self.bounds.size;;
      CGFloat scale = [UIScreen mainScreen].scale;
    ///異步繪制:切換至子線程
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        UIGraphicsBeginImageContextWithOptions(size, NO, scale);
        ///獲取當(dāng)前上下文
        CGContextRef context = UIGraphicsGetCurrentContext();
        ///將坐標(biāo)系反轉(zhuǎn)
        CGContextSetTextMatrix(context, CGAffineTransformIdentity);
        ///文本沿著Y軸移動
        CGContextTranslateCTM(context, 0, size.height);
        ///文本反轉(zhuǎn)成context坐標(biāo)系
        CGContextScaleCTM(context, 1.0, -1.0);
        ///創(chuàng)建繪制區(qū)域
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathAddRect(path, NULL, CGRectMake(0, 0, size.width, size.height));
        ///創(chuàng)建需要繪制的文字
        NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:self.asynText];
        [attStr addAttribute:NSFontAttributeName value:self.asynFont range:NSMakeRange(0, self.asynText.length)];
        [attStr addAttribute:NSBackgroundColorAttributeName value:self.asynBGColor range:NSMakeRange(0, self.asynText.length)];
        ///根據(jù)attStr生成CTFramesetterRef
        CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attStr);
        CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, attStr.length), path, NULL);
        ///將frame的內(nèi)容繪制到content中
        CTFrameDraw(frame, context);
        UIImage *getImg = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        ///子線程完成工作, 切換到主線程展示
        dispatch_async(dispatch_get_main_queue(), ^{
            self.layer.contents = (__bridge id)getImg.CGImage;
        });
    });
}

@end

#import "ViewController.h"
#import "AsyncLabel.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    AsyncLabel *asLabel = [[AsyncLabel alloc] initWithFrame:CGRectMake(50, 100, 200, 200)];
    asLabel.backgroundColor = [UIColor cyanColor];
    asLabel.asynBGColor = [UIColor greenColor];
    asLabel.asynFont = [UIFont systemFontOfSize:16 weight:20];
    asLabel.asynText = @"學(xué)習(xí)異步繪制相關(guān)知識點(diǎn), 學(xué)習(xí)異步繪制相關(guān)知識點(diǎn)";
    [self.view addSubview:asLabel];
    ///不調(diào)用的話不會觸發(fā) displayLayer方法
    [asLabel.layer setNeedsDisplay];
}

@end

示例代碼下載
日常開發(fā)中推薦YYKit作者出的YYAsyncLayer


參考文檔:
iOS UIGraphicsGetCurrentContext()的使用
iOS CoreText原理及基本使用方法
iOS 圖片的解碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晚凿,一起剝皮案震驚了整個濱河市社露,隨后出現(xiàn)的幾起案子少梁,更是在濱河造成了極大的恐慌,老刑警劉巖拓颓,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡胚宦,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門燕垃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來枢劝,“玉大人,你說我怎么就攤上這事卜壕∧裕” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵轴捎,是天一觀的道長鹤盒。 經(jīng)常有香客問我,道長侦副,這世上最難降的妖魔是什么侦锯? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮跃洛,結(jié)果婚禮上率触,老公的妹妹穿的比我還像新娘。我一直安慰自己汇竭,他們只是感情好葱蝗,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著细燎,像睡著了一般两曼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上玻驻,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天悼凑,我揣著相機(jī)與錄音偿枕,去河邊找鬼。 笑死户辫,一個胖子當(dāng)著我的面吹牛渐夸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播渔欢,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼墓塌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了奥额?” 一聲冷哼從身側(cè)響起苫幢,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎垫挨,沒想到半個月后韩肝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡九榔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年哀峻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哲泊。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡谜诫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出攻旦,到底是詐尸還是另有隱情,我是刑警寧澤生逸,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布牢屋,位于F島的核電站,受9級特大地震影響槽袄,放射性物質(zhì)發(fā)生泄漏烙无。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一遍尺、第九天 我趴在偏房一處隱蔽的房頂上張望截酷。 院中可真熱鬧,春花似錦乾戏、人聲如沸迂苛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽三幻。三九已至,卻和暖如春呐能,著一層夾襖步出監(jiān)牢的瞬間念搬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留朗徊,地道東北人首妖。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像爷恳,于是被迫代替她去往敵國和親有缆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345