iOS里面使用圆角有可能造成离屏渲染,它需要开辟一个新的内存空间,做上下文切换(状态切换),并且渲染完成后还要进行拷贝操作,因此会造成一定的性能损耗,需要进行优化。

1 原理

https://blog.bombox.org/2020-07-14/ios-offscreen-render/
这篇文章讲了离屏渲染的原理,非常清晰易懂。

离屏渲染的检测方法:

打开最新的Xcode(14.2)运行App
选菜单:Debug -> View Debugging -> Rendering -> Color Offscreen-Rendered Yellow
打开这个选项,看到App里面View的绿色或者黄色就说明发生了离屏渲染。

简单总结产生圆角离屏渲染的条件就是两点:多图层和裁剪

1.1 多图层

当前View有一下几种情况之一就会使用到多图层:

  1. 设置了background
  2. layer添加了子layer
  3. layer设置了mask
  4. 添加了子View

1.2 圆角

总共有4种方法可以产生圆角:

  1. masksToBounds裁剪
  2. layer.mask裁剪
  3. layer绘制圆角
  4. layer绘制圆角补角

4种方法示例如下,灰色是背景色,红色是添加的layer颜色
圆角

masksToBounds裁剪
这种方法是最简单常用的,它只能绘制最简单的圆角,作用于单图层时不会产生离屏渲染

        layer.cornerRadius = 20
        layer.masksToBounds = true

layer.mask裁剪
这种方法能绘制更复杂的圆角遮罩,但是它直接产生离屏渲染

        let maskPath = UIBezierPath(...)        
        let maskLayer = CAShapeLayer()
        maskLayer.path = maskPath.cgPath
        layer.mask = maskLayer

layer绘制圆角
这种方法是生成一个圆角layer,不会产生离屏渲染

        let layer = CAShapeLayer()
        layer.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
        layer.path = UIBezierPath(roundedRect: layer.frame, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: radii, height: radii)).cgPath
        layer.fillColor = UIColor.red.cgColor

layer绘制圆角补角
这种方法是生成圆角的四个补角的图形layer盖在最上层,不会产生离屏渲染,使用前提是圆角补角的下层或者说背景必须是纯色的

        let w:CGFloat = 200
        let h:CGFloat = 200
        let a90 = Double.pi * 0.5 // 90度

        let path = UIBezierPath()
        
        path.move(to: CGPoint(x: 0, y: radii))
        path.addArc(withCenter: CGPoint(x: radii, y: radii), radius: radii, startAngle: -a90 * 2, endAngle: -a90, clockwise: true)
        path.addLine(to: .zero)
        path.close()
        
        path.move(to: CGPoint(x: w - radii, y: 0))
        path.addArc(withCenter: CGPoint(x: w - radii, y: radii), radius: radii, startAngle: -a90, endAngle: 0, clockwise: true)
        path.addLine(to: CGPoint(x: w, y: 0))
        path.close()
        
        path.move(to: CGPoint(x: w, y: h - radii))
        path.addArc(withCenter: CGPoint(x: w - radii, y: h - radii), radius: radii, startAngle: 0, endAngle: a90, clockwise: true)
        path.addLine(to: CGPoint(x: w, y: h))
        path.close()
        
        path.move(to: CGPoint(x: radii, y: h))
        path.addArc(withCenter: CGPoint(x: radii, y: h - radii), radius: radii, startAngle: a90, endAngle: a90 * 2, clockwise: true)
        path.addLine(to: CGPoint(x: 0, y: h))
        path.close()
        
        let layer = CAShapeLayer()
        layer.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
        layer.path = path.cgPath
        layer.fillColor = UIColor.red.cgColor

2 圆角场景和使用

对于不同场景,使用不同的圆角方案:

  • 没有子layer或子View的矩形圆角,用masksToBounds;
  • 非矩形圆角或者有子layer子View,用layer绘制圆角;
  • 圆角layer无法盖住时(比如圆角渐变,CAGradientLayer不能直接做圆角),用layer绘制圆角补角覆盖;
  • 如果背景不是纯色,并且用layer绘制圆角补角有问题,就只能用layer.mask裁剪

注意:使用layer的圆角在View的大小变化时需要重新绘制,可以在layoutSubviews或者layoutSublayers方法里做

版权声明:本文为rome753原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/rome753/p/17055930.html