手机性能优化的重点, 就是界面渲染. 一般, 计算任务都交给服务端.
界面渲染慢, 就不好了.
常见问题, 就是离屏渲染. 这里用 NSShadow 处理掉 CALayer 的阴影属性带来的离屏渲染.
常见的离屏渲染代码: 绘制阴影,
- var label = UILabel()
- label.layer.shadowColor = UIColor.lightGray.cgColor
- label.layer.shadowOffset = CGSize(width: 0.0, height: 5.0)
- label.layer.shadowOpacity = 1.0
- label.layer.shadowRadius = 5.0
- label.text = "离屏渲染"
写完以后, CPU 和 GPU 都没有充足的信息绘制阴影效果.
过程是, CPU 会先把文本传出去, 请求 GPU (CPU 把文本传给 GPU ), 创建一个内存中的位图上下文 (GPU 把文本放进去 ), 离屏渲染这就开始了.
这个上下文缺信息, 不在屏幕上, 不是帧缓冲.(渲染出来的图形上下文, 不属于当前帧).
之前 CPU 把文本处理好了, 现在 CPU 处理文本效果 (这里是阴影).
然后, CPU 拿到渲染好的文字, 基于渲染出来的每一个像素的透明度, 计算出阴影的形状.
最后, CPU 把最新计算出来的文字阴影形状的信息, 传给 GPU . GPU 有阴影信息, 有之前图形上下文的渲染文本, GPU 就渲染好了最终的文字及其阴影, 交给帧缓冲 (Frame Buffer), 我们就看到了.
这段代码要渲染两次, 出现了离屏渲染. 对 GPU 的性能有影响. 他需要等待 CPU 来算出阴影的形状.
因为我们要 60 的帧数 ( FPS ), GPU 准备帧缓冲, 渲染出当前帧, 只有 17 毫秒的时间. 拖累了主线程, 屏幕刷新不过来.
对于图形阴影, 用 layer 的 shadowPath. 对于文字阴影, 用 NSShadow .
layer 的四个属性 shadowColor , shadowOffset ,shadowOpacity ,shadowRadius , 一般性能不好.
对于一个视图框, 通过 layer 在周边加阴影. 用 layer 的 shadowPath, 创建一个 UIBezierPath,
- UIBezierPath(rect: CGRect(x: 0, y: 0, width: 50, height: 50))
- // size of your label
与之前不同, 图层不用渲染两次. 现在 GPU 有了足够的信息绘制阴影效果, 就不用离屏渲染了.
对于文字阴影, 用 NSShadow , 用 layer.path 就比较难.
UI 性能优化主要用的是 Instruments 的 Core Animation 模版.
过去, Core Animation 模版几个调试选项非常强大, 正常的绿色, 异常的红色, 离屏渲染的黄色.
(光栅化有效, 光栅化后缓存的内容成功复用. 界面会显示绿色.
光栅化失效的部分, 呈红色. 光栅化后缓存的内容没有复用, 光栅化隐式创建的位图浪费了. 直接重新绘制. )
现在这些利器都在 Xcode 里了, 可以直接使用.
真机运行直接选择,
本文 Demo 使用的是 500 px https://github.com/500px/legacy-api-documentation 的 API .
调试界面的离屏渲染, 用的是
Color Offscreen-Rendered Yellow
选项.
调试目标是, 出现大片的绿色.
这里可以用 Apple 的 UIKit 框架下的 NSShadow 对象.
- let shadow = NSShadow()
- shadow.shadowColor = UIColor.lightGray
- shadow.shadowOffset = CGSize(width: 0.0, height: 5.0)
- shadow.shadowBlurRadius = 5.0
- if let mutableAttributedString = label.attributedText as? NSMutableAttributedString{
- let range = NSRange(location: 0, length: mutableAttributedString.string.count)
- mutableAttributedString.addAttribute(NSAttributedString.Key.shadow, value: shadow, range: range)
- }
用背景色处理掉混色.
界面图层混色, 就是多个视图的位置有重叠, 他们又不是透明的. 重叠区域的每一个像素, GPU 需要算出一种新的颜色 (混色). 如果这种效果, 不是 UI 设计的, 尽量避免.
调试界面的图层混色, 用的是
Color Blended Layer
选项.
UILabel 建议设置背景色. UILabel 有文字, 那就不透明, Label 默认的背景色是透明色, 与父视图的背景色, 混杂了. GPU 对相关位置的颜色, 需要重新计算.
- override func awakeFromNib() {
- super.awakeFromNib()
- [userNameLabel, photosLikeLabel, photosDescriptionLabel, photoTimeIntevalSincePostLabel].forEach {
- $0?.backgroundColor = UIColor.white
- }
- }
设置后, 效果明显
混色, 如果不是 UI 指定的效果, 建议处理掉.
界面图层混色, 常见的影响因素是 view 的 alpha 属性. alpha 小于 1, 一般自带混色效果.
相关代码: https://github.com/BoxDengJZ/Instruments_Wen
其他知识点介绍: 卡顿 (丢帧).
Instruments 的 Core Animation 模版的时间线, 就是 FPS. 显示随着时间, App 的帧率波动.
可以方便的读取帧数, 直观的了解那些界面要改善.
用代码检测卡顿, 自然是 CADisplayLink , 网上相关博客很多.
相关资源:
视频教程, practical-instruments
Apple, Performance Tips
来源: https://juejin.im/post/5bf26cd36fb9a049a5707fdd