前言
在最初写这篇的时候,并没有考虑到要写「iOS绘制方案调研」为题的一篇的文章,原来只是想记录在悦跑圈4.0开发中遇到的一个关于截图耗时特别长的一个问题,随即在写问题记录的时候,写下来的模拟demo结果却出乎意料,然后决定在以后会写一篇「iOS绘制方案调研」。
正文
在开发悦跑圈4.0版本的时候,遇到了一个很有意思的问题,需要将跑步路线的经纬度在View上绘制一段轨迹,这里的一个原来的做法是先轨迹绘制在一个View上,然后截图传给水印相机的VC。
主要的几个方法如下:
override func draw(_ rect: CGRect) {
let bezier = setupPath()
bezier.lineWidth = 10
UIColor.black.setStroke()
bezier.lineJoinStyle = .round
bezier.lineCapStyle = .round
bezier.stroke()
}
func getImageForView() -> UIImage? {
UIGraphicsBeginImageContext(bounds.size)
guard let context = UIGraphicsGetCurrentContext() else { return nil }
layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
这时候问题就来了,测试同学反馈某条测试数据卡顿高达5、6分钟,在iPhone6以下的机型大概出现闪退的情况。 debug一轮后,发现问题是卡顿是出在获取图片的方法:
layer.render(in: context)
在调用这个方法的时候,CPU使用率长期在90%以上
至于原因,在stackoverflow上已经有人给出的答案了,renderInContext:
是在CPU上运行的,在iOS 7之后使用drawHierarchy(in: , afterScreenUpdates: )
会更快。参考链接
将原来的截图方法,换成了
func getImageForView(afterScreenUpdates:Bool) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0)
drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
效果依旧不明显,还是非常耗时。
搜索一番后,没有找到合适的解决方法后,我决定开始自己定位问题,寻找解决方案。
原来的绘制是使用CGContext
,要知道CGContext
的绘制是调用Core Graphics
的方法实现,Core Graphics
占用CPU和消耗内存。
像是绘制这种轨迹的需求,其实除了drawRect以外,还有一个方案,那就是CAShapeLayer
。
CAShapeLayer需要通过贝塞尔曲线的CGPath,然后提交到GPU上渲染。
CAShapeLayer
通过GPU渲染图形,不消耗内存。
另外有部分文章指出:
“CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多。”
这一点,在我写demo的时候发现了并不是这样,即使是相同的path,drawRect也是有可能会比CAShapeLayer要快的。关于这一点,我会把这点放在iOS绘制方案调研去讲。
在使用CAShapeLayer
和drawHierarchy(in: , afterScreenUpdates: )
之后,原本一段需要5、6分钟才能生成的轨迹,现在只需要1秒就就可以生成出来了。业务问题的确已经解决了,但是绘制的问题并没有结束,我在文章开头就已经写了,在写这个Demo的时候,测试的结果确出乎意料,这部分的讨论将会放在下篇去讨论。
请保持转载后文章内容的完整,以及文章出处。本人保留所有版权相关权利。