1. UIView的繪制流程圖
-
UIView
調(diào)用setNeedsDisplay
,但是沒立即進(jìn)行視圖的繪制工作; -
UIView
調(diào)用setNeedDisplay
后,系統(tǒng)調(diào)用view
對應(yīng)layer
的setNeedsDisplay
方法; - 當(dāng)前
runloop
即將結(jié)束的時候調(diào)用CALayer
的display
方法; -
runloop
即將結(jié)束, 開始視圖的繪制流程;
知識點(diǎn):為什么調(diào)用
UIView
的setNeedsDisplay
后界面并沒有立即繪制?
在當(dāng)前Runloop
將要結(jié)束的時候才會開始界面的繪制;
2. 系統(tǒng)繪制流程圖
1.
CALayer
內(nèi)部創(chuàng)建一個backing store(CGContextRef
)();
- 判斷l(xiāng)ayer是否有代理;
2.1 有代理:調(diào)用delegete
的drawLayer:inContext
, 然后在合適的實(shí)際回調(diào)代理, 在[UIView drawRect]
中做一些繪制工作;
2.2 沒有代理:調(diào)用layer
的drawInContext
方法, -
layer
上傳backingStore
到GPU
, 結(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ù)的處理邏輯, 每次圖片下載后是先在子線程將圖片解碼成位圖, 然后用的時候在主線程展示;
- 某個時機(jī)調(diào)用
setNeedsDisplay
; -
runloop
將要結(jié)束的時候調(diào)用[CALayer display]
- 如果代理實(shí)現(xiàn)了
dispalyLayer
將會調(diào)用此方法, 在子線程中去做異步繪制的工作; - 子線程中做的工作:創(chuàng)建上下文, 控件的繪制, 生成圖片;
- 轉(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 圖片的解碼