UI绘制原理

作者: AcmenL | 来源:发表于2021-03-17 22:27 被阅读0次

1、UIView的绘制流程

对这个流程进行下说明:

1、 当我们调用[UIView setNeedsDisplay]方法时,并没有执行立即执行绘制工作;

2、 而是马上调用[view.layer setNeedsDisplay]方法,给当前layer打上脏标记;

3、当前RunLoop快要结束的时候调用[layer display]方法,来进入到当前视图的真正绘制当中;

4、[layer display]方法内部,系统会判断layer的layer.delegate是否能响应[layer.delegate respondsToSelector:@selector(displayLayer:)]方法,
a. 如果没有实现,则执行系统的绘制流程;
b. 如果实现了则会进入异步绘制的入口;

5、 最后把绘制完的backing store(可以理解为位图)提交给GPU

2、系统绘制流程

系统绘制流程

对这个流程进行下说明:

1、layer内部创建一个backing store,我们可以理解为CGContextRef上下文;

2、 判断layer是否有delegate
a. 如果有delegate,则会执行[layer.delegate drawLayer:inContext](这个方法的执行是在系统内部执行的),然后在这个方法中会调用[view drawRect:]方法,也就是我们重写view的drawRect:方法才会被调用到。
b. 如果没有delegate,会调用layer的drawInContext方法,也就是我们可以重写的layer的该方法,此刻会被调用到

3、 最后把绘制完的backing store(可以理解为位图)提交给GPU。

3、异步绘制实现

异步绘制实现

1、 某个时机调用setNeedsDisplay;
2、 runloop将要结束时调用[CALayer display];
3、 若代理实现了displayLayer将会调用此方法,在子线程中做异步绘制的工作;
4、 在子线程中创建上下文、绘制控件并生成图片;
5、 在主线程中设置layer.contents,将生成的视图展示在layer上。

示例:

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface AsyncDrawLabel : UIView

@property (nonatomic, copy) NSString *text;
@property (nonatomic, strong) UIFont *font;

@end

NS_ASSUME_NONNULL_END
#import "AsyncDrawLabel.h"
#import <CoreText/CoreText.h>

@implementation AsyncDrawLabel

- (void)setText:(NSString *)text {
    _text = text;
}

- (void)setFont:(UIFont *)font {
    _font = font;
}


// 除了在drawRect方法中, 其他地方获取context需要自己创建[https://www.jianshu.com/p/86f025f06d62] coreText用法简介:[https://www.cnblogs.com/purple-sweet-pottoes/p/5109413.html]
 
- (void)displayLayer:(CALayer *)layer {
    CGSize size = self.bounds.size;
    CGFloat scale = [UIScreen mainScreen].scale;
    // 异步绘制,切换至子线程
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        UIGraphicsBeginImageContextWithOptions(size, NO, scale);
        // 获取当前上下文
        CGContextRef context = UIGraphicsGetCurrentContext();
        [self draw:context size:size];
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        // 子线程完成工作,切换至主线程显示
        dispatch_async(dispatch_get_main_queue(), ^{
            self.layer.contents = (__bridge id)image.CGImage;
        });
    });
}

- (void)draw:(CGContextRef)context size:(CGSize)size {
    // 将坐标系上下翻转,因为底层坐标系和 UIKit 坐标系原点位置不同。
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    // 文本沿着Y轴移动
    CGContextTranslateCTM(context, 0, size.height); // 原点为左下角
    // 文本反转成context坐标系
    CGContextScaleCTM(context, 1, -1);
    // 创建绘制区域
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, CGRectMake(0, 0, size.width, size.height));
    // 创建需要绘制的文字
    NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc]initWithString:self.text];
    [attrStr addAttribute:NSFontAttributeName value:self.font range:NSMakeRange(0, self.text.length)];
    // 根据attStr生成CTFramesetterRef
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrStr);
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attrStr.length), path, NULL);
    // 将frame的内容绘制到content中
    CTFrameDraw(frame, context);
}

@end
#import "ViewController.h"
#import "AsyncDrawLabel.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    AsyncDrawLabel *label = [[AsyncDrawLabel alloc] initWithFrame:CGRectMake(100, 100, 200, 100)];
    label.backgroundColor = [UIColor yellowColor];
    label.text = @"异步绘制text";
    label.font = [UIFont systemFontOfSize:16];
    [self.view addSubview:label];
    [label.layer setNeedsDisplay]; // 不调用的话不会触发displayLayer方法
}

@end

相关文章

  • iOS体系

    UI视图 UITableView 事件传递&视图响应 图像显示原理 UI卡顿、掉帧 UI绘制原理/异步绘制 离屏渲...

  • 常见面试问题概括

    UI视图相关 *TableView重用机制? 答: *视图绘制原理?如何实现异步绘制? 答:UIView绘制原理 ...

  • 无标题文章

    APP性能优化 UI卡顿优化 View的绘制原理 UI卡顿原理分析 UI卡顿检测分析 BlockCanary原理分...

  • iOS面试必看

    01UI视图 事件传递机制UI绘制原理异步绘制原理流式页面的性能优化离屏渲染 02OC语言 KVOKVC分类关联对...

  • # UI视图相关

    UI视图相关 UITableView 事件传递&视图响应 图像显示原理 卡顿&掉帧 绘制原理&异步绘制 离屏渲染 ...

  • 布局优化

    1Android绘制原理及工具选择 绘制原理 CPU负责计算显示内容GPU负责柵格化(UI元素绘制到屏幕上) 系统...

  • UI视图

    UI视图考点: UITableView相关 事件传递&视图响应 图像显示原理 卡顿&掉帧 绘制原理&异步绘制 离屏渲染

  • UI绘制原理

    当调用UIView的setNeedsDisplay方法时,会调用CALayer的同名方法setNeedsDispl...

  • UI绘制原理

    1、UIView的绘制流程 对这个流程进行下说明: 1、 当我们调用[UIView setNeedsDisplay...

  • iOS面试题——UI相关:事件传递,图像显示,性能优化,离屏渲染

    UIView与CALayer 事件传递与视图响应链 图像显示原理 UI卡顿掉帧原因 滑动优化方案 UI绘制原理 离...

网友评论

    本文标题:UI绘制原理

    本文链接:https://www.haomeiwen.com/subject/jmescltx.html