1.什么是离屏渲染
说到这个问题,就不得先了解一下图像渲染的框架和它的渲染流程.
iOS的渲染框架如下图:
需要经过一下几个阶段:
通常情况下,我们渲染一个视图的流程如下图:
2.为什么要使用离屏渲染
-
一些特殊效果需要使用额外的 Offscreen Buffer来保存渲染的中间状态,所以不得不使用离屏渲染,比如毛玻璃、阴影等需要叠加合成实现完成效果的情况下。
-
处于效率目的,可以将内容提前渲染保存在 Offscreen Buffer 中,达到复用的目的,也就是打开layer的shouldRasterize。开启光栅化后,会触发离屏渲染,Render Server 会强制将 CALayer 的渲染位图结果 bitmap 保存。这个时候bitmap会保存在CPU中。而保存的 bitmap 包含 layer 的 subLayer、圆角、阴影、组透明度 group opacity 等,所以如果 layer 的构成包含上述几种元素,结构复杂且需要反复利用,那么就可以考虑打开光栅化。
使用shouldRasterize注意以下几点
- 如果 layer 不能被复用,则没有必要打开光栅化
- 如果 layer 不是静态,需要被频繁修改,比如处于动画之中,那么开启离屏渲染反而影响效率
- 离屏渲染缓存内容有时间限制,缓存内容 100ms 内如果没有被使用,那么就会被丢弃,无法进行复用
- 离屏渲染缓存空间有限,超过 2.5 倍屏幕像素大小的话也会失效,无法复用
4.触发离屏渲染的场景
-
使用了 mask 的 layer (layer.mask)
-
需要进行裁剪的 layer (layer.masksToBounds / view.clipsToBounds)
-
设置了组透明度为 YES,并且透明度不为 1 的 layer (layer.allowsGroupOpacity/layer.opacity)
-
添加了投影的 layer (layer.shadow*)
-
采用了光栅化的 layer (layer.shouldRasterize)
-
绘制了文字的 layer (UILabel, CATextLayer, Core Text 等)
4.如何避免离屏渲染
- 【换资源】直接使用带圆角的图片,或者替换背景色为带圆角的纯色背景图,从而避免使用圆角裁剪。不过这种方法需要依赖具体情况,并不通用
- 【mask】再增加一个和背景色相同的遮罩 mask 覆盖在最上层,盖住四个角,营造出圆角的形状。但这种方式难以解决背景色为图片或渐变色的情况
- 【UIBezierPath】用贝塞尔曲线绘制闭合带圆角的矩形,在上下文中设置只有内部可见,再将不带圆角的 layer 渲染成图片,添加到贝塞尔矩形中。这种方法效率更高,但是 layer 的布局一旦改变,贝塞尔曲线都需要手动地重新绘制,所以需要对 frame、color 等进行手动地监听并重绘
- 【CoreGraphics】重写 drawRect:,用 CoreGraphics 相关方法,在需要应用圆角时进行手动绘制。不过 CoreGraphics 效率也很有限,如果需要多次调用也会有效率问题