iOS锥形边框渐变动画异常:闪烁问题求解决方案
多色锥形渐变边框动画闪烁问题修复方案
问题根源
闪烁问题主要源于三点:
Timer触发动画与Core Animation周期不同步,导致衔接断层- 仅动画
colors属性未同步动画locations,多色渐变下位置不匹配引发闪烁 - 频繁在
CATransaction完成块中移除、添加动画,造成图层状态切换的视觉跳变
修复代码
class ViewController: UIViewController { private enum Color { static var gradientColors: [UIColor] = [ .white, .systemBlue, .systemPink, .systemGreen, UIColor(white: 1, alpha: 0) ] } @IBOutlet weak var gradientView: UIView! { didSet { gradientView.layer.cornerRadius = 16 } } private var gradientLayer: CAGradientLayer! override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) setupAnimatedBorder() } private func setupAnimatedBorder() { // 创建遮罩形状层 let shapeLayer = CAShapeLayer() shapeLayer.path = UIBezierPath( roundedRect: gradientView.bounds.insetBy(dx: 1.0, dy: 1.0), cornerRadius: gradientView.layer.cornerRadius ).cgPath shapeLayer.lineWidth = 1.0 shapeLayer.strokeColor = UIColor.white.cgColor shapeLayer.fillColor = UIColor.clear.cgColor // 初始化锥形渐变层 gradientLayer = CAGradientLayer() gradientLayer.frame = gradientView.bounds gradientLayer.type = .conic gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.5) gradientLayer.endPoint = CGPoint(x: 1, y: 1) gradientLayer.mask = shapeLayer gradientView.layer.addSublayer(gradientLayer) // 启动循环动画 startGradientAnimation() } private func startGradientAnimation() { let colorCount = Color.gradientColors.count // 生成对应偏移量的位置数组 func locations(forOffset offset: Int) -> [NSNumber] { let baseValues = stride(from: 0, to: colorCount, by: 1).map { Double($0) + Double(offset) } return baseValues.map { NSNumber(value: fmod($0, Double(colorCount)) / Double(colorCount)) } } // 生成颜色滚动的关键帧 let colorKeyframes = (0...colorCount).map { index in let shiftedColors = Array(Color.gradientColors[index...] + Color.gradientColors[..<index]) return shiftedColors.map { $0.cgColor } } let colorAnimation = CAKeyframeAnimation(keyPath: "colors") colorAnimation.values = colorKeyframes colorAnimation.duration = 2.0 colorAnimation.repeatCount = .infinity colorAnimation.isRemovedOnCompletion = false // 生成位置同步的关键帧 let locationKeyframes = (0...colorCount).map { locations(forOffset: $0) } let locationAnimation = CAKeyframeAnimation(keyPath: "locations") locationAnimation.values = locationKeyframes locationAnimation.duration = 2.0 locationAnimation.repeatCount = .infinity locationAnimation.isRemovedOnCompletion = false // 组合动画确保同步 let animationGroup = CAAnimationGroup() animationGroup.animations = [colorAnimation, locationAnimation] animationGroup.duration = 2.0 animationGroup.repeatCount = .infinity animationGroup.timingFunction = CAMediaTimingFunction(name: .linear) // 设置初始状态并添加动画 gradientLayer.colors = Color.gradientColors.map { $0.cgColor } gradientLayer.locations = locations(forOffset: 0) gradientLayer.add(animationGroup, forKey: "gradientScroll") } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) // 页面消失时清理动画 gradientLayer?.removeAllAnimations() } }
核心改动说明
- 替换Timer为原生动画循环:利用Core Animation的
repeatCount = .infinity实现无限循环,避免定时器与动画引擎的同步问题 - 同步动画colors与locations:多色渐变中颜色位置必须随颜色数组同步偏移,否则会出现颜色断层闪烁
- 关键帧动画实现平滑滚动:预先生成所有颜色偏移的关键帧,让颜色过渡无断层
- 简化图层结构:仅保留一个渐变层,减少图层数量降低性能消耗
- 添加动画清理逻辑:避免视图销毁后残留动画导致的内存泄漏
内容的提问来源于stack exchange,提问作者Novice
相关产品推荐
相关产品推荐

