圆角是iOS系统中常见的视觉样式,从系统图标到导航栏按钮,圆角无处不在。因为圆角是符合人类视觉安全体验的,圆角让人觉得舒适,而尖角在潜意识层次是具有伤害体验的,因为尖尖的东西总是有可能对人造成伤害的,所以我们更喜欢圆角。在iOS开发过程中,苹果提供了一种添加圆角的方法,简单暴力有效,但并不是所有的开发者都清楚原理,因此设置圆角有时会带来一定的性能损耗。本文将从理论上介绍设置圆角方法,性能损耗的原因这两方面展开讨论。下一篇文章将从具体项目的实践中介绍如何避免性能损耗。
一种设置圆角的简单方法
在iOS开发过程中,有一种非常简单的方法,只需一行代码,就可以给UIView
加上圆角效果。
view.layer.cornerRadius = 5;
这一行代码并不会带来任何性能上的损耗,然而理想和现实是有差距的,并不是所有的UIView
都可以只通过一行代码添加上圆角。我们来看苹果对cornerRadius
这一属性的解释:
By default, the corner radius does not apply to the image in the layer’s contents property; it applies only to the background color and border of the layer. However, setting the masksToBounds property to YES causes the content to be clipped to the rounded corners.
在通常情况下,cornerRadius
属性只应用于layer
的背景色和边线。将masksToBounds
属性设置为YES
才能把内容按圆角形状裁剪。
也就是说,如果UIView
中含有子视图,还需要设置masksToBounds
属性为YES
,才能实现圆角。于是代码变成了两行:
view.layer.cornerRadius = 5;
view.layer.masksToBounds = YES;
就是这一行设置masksToBounds
的代码,让我们的显示帧率蹭蹭往下掉。如果屏幕中同时显示的圆角个数过多,就会明显感觉到卡顿和跳帧。这是为什么呢?
性能损耗:掉帧
如果我们打开模拟器的Color Offscreen-Rendered Yellow
选项,运行代码后会发现,在我们绘制圆角的地方出现了黄色的标记。黄色标记出现的地方即离屏渲染。
在解释离屏渲染之前,先来介绍一点背景知识:
UIView和CALayer的关系
简单地说,UIView
是对CALayer
的封装。CALayer
包含三个基本视觉元素:border
(属性包括宽度,颜色),background
(属性包括颜色)和contents
,其中contents
必须是一个CGImage
。
回到UIView和CALayer的关系:CALayer
的contents
负责显示内容,同时UIView
为其提供内容,以及负责处理触摸等事件,参与响应链。
离屏渲染
关于离屏渲染的介绍,可以参考这篇文章。文中提到:
当使用圆角,阴影,遮罩的时候,图层属性的混合体被指定为在未预合成之前不能直接在屏幕中绘制,所以就需要屏幕外渲染被唤起。
屏幕外渲染并不意味着软件绘制,但是它意味着图层必须在被显示之前在一个屏幕外上下文中被渲染(不论CPU还是GPU)。
所以当使用离屏渲染的时候会很容易造成性能消耗,因为在OPENGL里离屏渲染会单独在内存中创建一个屏幕外缓冲区并进行渲染,而屏幕外缓冲区跟当前屏幕缓冲区上下文切换是很耗性能的。
上文提到,cornerRadius
属性只对前景框和背景色起作用,再看CALayer
的结构,如果contents
有内容或者内容的背景不是透明的话,还需要把这部分弄个角出来,不然合成的结果还是没有圆角,所以才要修改masksToBounds
为YES
(在UIView
上对应的属性是clipsToBounds
)。前些日子很热闹的圆角优化文章中的2篇指出是修改masksToBounds
为YES
而非修改cornerRadius
才是触发离屏渲染的原因,但如果以Color Offscreen-Renderd Yellow
的特征为标准的话,这两个属性单独作用时都不是引发离屏渲染的原因,他俩合体才是。
简单来说,卡顿和跳帧的原因是我们设置圆角的两行代码,触发系统对圆角进行离屏渲染,进而影响了帧率。
总结
- 离屏渲染使用不当会导致性能问题。
- 只设置
cornerRadius
时不会触发离屏渲染,仅适用于特殊情况:contents
为空或者contents
不会遮挡背景色圆角。 - 打开模拟器的
Color Offscreen-Rendered Yellow
选项,可以检测离屏渲染。