先放结论
-
如果能够只用 cornerRadius 解决问题,就不用优化。
-
如果必须设置 masksToBounds,可以参考圆角视图的数量,如果数量较少(一页只有几个)也可以考虑不用优化。
-
UIImageView 的圆角通过直接截取图片实现,其它视图的圆角可以通过 Core Graphics 画出圆角矩形实现。
GPU渲染机制:
CPU 计算好显示内容提交到 GPU,GPU 渲染完成后将渲染结果放入帧缓冲区,随后视频控制器会按照 VSync 信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器显示。
GPU屏幕渲染有以下两种方式:
-
On-Screen Rendering
意为当前屏幕渲染,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行。 -
Off-Screen Rendering
意为离屏渲染,指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。
离屏渲染的触发方式
设置了以下属性时,都会触发离屏绘制:
- shouldRasterize(光栅化)
- masks(遮罩)
- shadows(阴影)
- edge antialiasing(抗锯齿)
- group opacity(不透明)
- 复杂形状设置圆角等
- 渐变
当使用圆角,阴影,遮罩的时候,图层属性的混合体被指定为在未预合成之前不能直接在屏幕中绘制,所以就需要屏幕外渲染被唤起。
屏幕外渲染并不意味着软件绘制,但是它意味着图层必须在被显示之前在一个屏幕外上下文中被渲染(不论CPU还是GPU)。
所以当使用离屏渲染的时候会很容易造成性能消耗,因为在OPENGL里离屏渲染会单独在内存中创建一个屏幕外缓冲区并进行渲染,而屏幕外缓冲区跟当前屏幕缓冲区上下文切换是很耗性能的。
Instruments监测离屏渲染
Instruments的Core Animation工具中有几个和离屏渲染相关的检查选项:
-
Color Offscreen-Rendered Yellow
开启后会把那些需要离屏渲染的图层高亮成黄色,这就意味着黄色图层可能存在性能问题。 -
Color Hits Green and Misses Red
如果shouldRasterize被设置成YES,对应的渲染结果会被缓存,如果图层是绿色,就表示这些缓存被复用;如果是红色就表示缓存会被重复创建,这就表示该处存在性能问题了。
iOS版本上的优化
iOS 9.0 之前UIimageView跟UIButton设置圆角都会触发离屏渲染
iOS 9.0 之后UIButton设置圆角会触发离屏渲染,而UIImageView里png图片设置圆角不会触发离屏渲染了,如果设置其他阴影效果之类的还是会触发离屏渲染的。
这可能是苹果也意识到离屏渲染会产生性能问题,所以能不产生离屏渲染的地方苹果也就不用离屏渲染了。
正常添加:label.layer.cornerRadius = 8。但通常我们还会加上:label.layer.masksToBounds = true。此时并不会有性能损耗的,但是如是设置Color Offscreen-Rendered Yellow会发现 label 的四周出现了黄色的标记,说明这里出现了离屏渲染。
但是根据大神的测试,离屏渲染并非由设置圆角导致的,因为 UIView 只是设置了 cornerRadius,但它没有出现离屏渲染,设置 masksToBounds 会导致离屏渲染,从而影响性能,但是影响并没有想象中的那么大,例如有大神测试 17 个带有圆角的视图,滑动时的帧数依然在 58 - 59 fps 左右波动,甚至当屏幕上有奖金40个的时候,才开始有点卡顿,fps 下降到 33 左右,此时才会影响用户体验。
高效地设置圆角
普通的 UIView 设置圆角,和为 UIImageView 设置圆角的原理不同
override func drawRect(rect: CGRect) {
let maskPath = UIBezierPath(roundedRect: rect, byRoundingCorners: .AllCorners, cornerRadii: CGSize(width: 3, height: 3)) let maskLayer = CAShapeLayer() maskLayer.frame = self.bounds maskLayer.path = maskPath.CGPath self.layer.mask = maskLayer }此段代码可以实现 cornerRadius = 3 的效果,但是并不是完美的代码,但是实际测试中发现,对drawRect的重写会严重影响内存(内存暴增)和用户体验(更加卡顿),这种方法本质上是用遮罩层 mask 来实现,因此同样无可避免的会导致离屏渲染,但是带来的副作用是相比之前的fps在屏幕上圆角控件多的情况下,下降是原来的一倍!