图层绘制
绘制主要是CPU绘制,运用Core Graphic技术,可以支持异步绘制(CoreGraphic已经支持线程安全)。UIView只是对CALayer的浅封装,CALayer负责绘制和显示,因此绘制的输出就是CALayer的contents属性。绘制的结果是一张bitmap,可以直接转换为纹理提交给GPU渲染。
绘制时机
当在操作 UI 时,比如改变了 Frame、更新了 UIView/CALayer 的层次时,或者手动调用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后,这个 UIView/CALayer 就被标记为待处理,并被提交到一个全局的容器去。
苹果注册了一个 Observer 监听 BeforeWaiting(即将进入休眠) 和 Exit (即将退出Loop) 事件,回调去执行一个很长的函数:
_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()。这个函数里会遍历所有待处理的 UIView/CAlayer 以执行实际的绘制和调整,并更新 UI 界面。
这个函数内部的调用栈大概是这样的:
绘制细节
CALayer提供了三种绘制内容的方式,若采用优先级高的方式绘制了内容,则低优先级的则不会执行。
每一个UIView都有一个layer,每一个layer都有个content,这个content指向的是一块缓存,叫做backing store。默认情况下,CALayer的content为空。若UIView的子类重写了drawRect,则UIView执行完drawRect后,系统会为器layer的content开辟一块缓存,缓存大小为size = width*height*scale
,用来存放drawRect绘制的内容。
即使重写的drawRect啥也没做,也会开辟缓存,消耗内存,所以尽量不要随便重写drawRect却啥也不做。举例说明:
-(void)drawRect:(CGRect)rect{
NSLog(@"MyView.before.drawRect.layer.contents %@",self.layer.contents);
// CGContextRef context = UIGraphicsGetCurrentContext();
// draw something
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"MyView.after.drawRect.layer.contents %@",self.layer.contents);
});
}
MyView* myView = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
[self.view addSubview:myView];
最后的输出结果(模拟器是7 plus,屏幕scale为3):
MyView.before.drawRect.layer.contents (null)
MyView.after.drawRect.layer.contents <CABackingStore 0x7fa1a7715fe0 (buffer [600 600] BGRX8888)>
当UIView被绘制时(从 CA::Transaction::commit:以后),CPU执行drawRect,通过context将数据写入backing store。当backing store写完后,通过render server交给GPU去渲染,将backing store中的bitmap数据显示在屏幕上 。
参考
OpenGL原理介绍
如何在iOS上创建矢量图形
Core Animation编程指南
视图渲染、CPU和GPU卡顿原因及其优化方案
UIView 绘制渲染机制
Core Animation高级理论知识汇总
网友评论