首先谈谈UIView
和CALayer
关系
UIView继承自UIResponder
,可以处理系统传递过来的事件,如:UIApplication
、UIViewController
、UIView
,以及所有从UIView
派生出来的UIKit类。每个UIView
内部都有一个CALayer
提供内容的绘制和显示,并且作为内部RootLayer
的代理视图。
CALayer
继承自NSObject
类,负责显示UIView
提供的内容contents
。CALayer
有三个视觉元素:背景色、内容和边框,其中,内容的本质是一个CGImage
。
下图为CALayer
的结构图:
CALayer结构.png
界面渲染过程
RunLoop
有一个60fps
的回调,即每16.7ms
绘制一次屏幕,所以view
的绘制必须在这个时间内完成,view
内容的绘制是CPU
的工作,然后把绘制的内容交给GPU
渲染,包括多个View
的拼接(Compositing
)、纹理的渲染(Texture
)等等,最后显示在屏幕上。但是,如果无法是16.7ms
内完成绘制,就会出现丢帧的问题,一般情况下,如果帧率保证在30fps
以上,界面卡顿效果不明显,那么就需要在33.4ms
内完成View
的绘制,而低于这个帧率,就会产生卡顿的效果,影响体验。
渲染的过程如下:
-
UIView
的layer
层有一个content
,指向一块缓存,即backing store
-
UIView
绘制时,会调用drawRect
方法,通过context
将数据写入backing store
- 在
backing store
写完后,通过render server
交给GPU
去渲染,将backing store
中的bitmap
数据显示在屏幕上
图2
离屏渲染
在使用圆角、阴影和遮罩等视图功能的时候,图层属性的混合体被指定为在未预合成之前不能直接在屏幕中绘制,所有就需要在屏幕外的上下文中渲染,即离屏渲染。
离屏渲染卡顿原因
离屏渲染之所以会特别消耗性能,是因为要创建一个屏幕外的缓冲区,然后从当屏缓冲区切换到屏幕外的缓冲区,然后再完成渲染;其中,创建缓冲区和切换上下文最消耗性能,而绘制其实不是性能损耗的主要原因。
设置了以下属性时,就会触发离屏绘制:
- shouldRasterize(光栅化)
- masks(遮罩)
- shadows(阴影)
- edge antialiasing(抗锯齿)
- group opacity(不透明)
- 复杂形状设置圆角等
- 渐变
对于复杂的页面,可以使用AsyncDisplayKit框架来做,或者不使用AutoLayout技术,以及减少UILabel的使用。
AsyncDisplayKit中使用了图层预合成技术,对于某些不需要响应手势且不会发生布局变化的内容,会在子线程统一合成一张图片再进行渲染。