iOS开发中渐变色的使用

59 阅读1分钟

 系统的动态渲染

使用CAGradientLayer + cornerRadius来实现动态渲染。

原理:由GPU进行实时的计算渐变,并且执行一些裁剪操作。但是两者结合会触发GPU离屏渲染

优缺点:占用内存极低,灵活。但是每次滚动的话就会反复触发离屏渲染就会导致消耗大量的GPU从而导致卡顿。

/**
     直接将一个渐变layer应用到视图上,并设置圆角。
     - Warning: 这种方式会触发GPU离屏渲染,适用于静态、不滚动的视图。
     - Parameters:
       - view: 目标视图。
       - colors: 渐变色数组。
       - startPoint: 渐变起始点 (范围 0-1)。
       - endPoint: 渐变结束点 (范围 0-1)。
       - cornerRadius: 圆角半径。
       - locations: 颜色分布位置 (可选, 范围 0-1)。
     */
    static func applyGradientLayer(
        to view: UIView,
        colors: [UIColor],
        startPoint: CGPoint,
        endPoint: CGPoint,
        cornerRadius: CGFloat,
        locations: [NSNumber]? = nil
    ) {
        // 移除旧的渐变层,防止重复添加
        view.layer.sublayers?.filter { $0 is CAGradientLayer }.forEach { $0.removeFromSuperlayer() }
        
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = view.bounds
        gradientLayer.colors = colors.map { $0.cgColor }
        gradientLayer.startPoint = startPoint
        gradientLayer.endPoint = endPoint
        gradientLayer.locations = locations
        
        // 应用到视图上
        view.layer.insertSublayer(gradientLayer, at: 0)
        
        // 设置圆角,这将与渐变层一起触发离屏渲染
        view.layer.cornerRadius = cornerRadius
        view.layer.masksToBounds = true
    }

转存失败,建议直接上传图片文件

烘焙方案

原理:这个实际上就是在CPU上将一切给渲染完成,然后进行烘焙操作,使其变成一张静态的UIImage。从而可以完全避免离屏渲染(当然前提是UIImage被裁剪完成没有再次触发裁剪操作)。

优缺点: CPU的负载极低,但是占用内存较高,并且在特殊情况下可能会导致失真,典型的拿空间换时间。

在使用这个的时候一定要避免一个重要的问题(使用了烘焙方案,又在使用的地方进行了圆角的裁剪导致了产生了离屏渲染,这样会使得性能消耗远远超越两者)

 /**
     将带有圆角的渐变色“烘焙”成一张 UIImage。
     - Note: 这种方式避免了离屏渲染,适用于需要高性能的滚动列表。
     - Parameters:
       - size: 要生成的图片尺寸。
       - colors: 渐变色数组。
       - startPoint: 渐变起始点 (范围 0-1)。
       - endPoint: 渐变结束点 (范围 0-1)。
       - cornerRadius: 圆角半径。
       - locations: 颜色分布位置 (可选, 范围 0-1)。
     - Returns: 一个带有渐变和圆角的 UIImage,或在参数无效时返回 nil。
     */
    static func createGradientImage(
        size: CGSize,
        colors: [UIColor],
        startPoint: CGPoint,
        endPoint: CGPoint,
        cornerRadius: CGFloat,
        locations: [NSNumber]? = nil
    ) -> UIImage? {
        let rect = CGRect(origin: .zero, size: size)
        if colors.isEmpty || rect.isEmpty {
            return nil
        }
​
        // 1. 创建一个临时的渐变Layer用于绘制
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = rect
        gradientLayer.colors = colors.map { $0.cgColor }
        gradientLayer.startPoint = startPoint
        gradientLayer.endPoint = endPoint
        gradientLayer.locations = locations
        
        // 如果需要圆角,我们利用 UIGraphics 来裁剪上下文
        // 而不是在 layer 上设置 cornerRadius,这样绘制结果本身就是带圆角的
        
        // 2. 使用 UIGraphicsImageRenderer 在CPU上进行绘制
        let renderer = UIGraphicsImageRenderer(size: size)
        let image = renderer.image { context in
            // 如果有圆角,先裁剪绘制区域
            if cornerRadius > 0 {
                let path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius)
                path.addClip()
            }
            // 将渐变Layer的内容渲染到当前图形上下文中
            gradientLayer.render(in: context.cgContext)
        }
        
        return image
    }

转存失败,建议直接上传图片文件