iOS UIView繪制&異步繪制

1. UIView的繪制流程圖

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

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


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


1.CALayer內(nèi)部創(chuàng)建一個(gè)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. 異步繪制

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

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

異步繪制流程
  1. 某個(gè)時(shí)機(jī)調(diào)用setNeedsDisplay;
  2. runloop將要結(jié)束的時(shí)候調(diào)用[CALayer display]
  3. 如果代理實(shí)現(xiàn)了dispalyLayer將會(huì)調(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://m.itdecent.cn/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軸移動(dòng)
        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)知識(shí)點(diǎn), 學(xué)習(xí)異步繪制相關(guān)知識(shí)點(diǎn)";
    [self.view addSubview:asLabel];
    ///不調(diào)用的話不會(huì)觸發(fā) displayLayer方法
    [asLabel.layer setNeedsDisplay];
}

@end

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


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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容