系统的动态渲染
使用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
}