You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS 为圆形视图添加Spinner动画时strokeEnd位置闪烁如何解决

圆形Spinner加载动画闪烁问题修复方案

问题现象

你实现的圆形加载动画效果如下,每轮动画走到3点钟位置时会出现闪烁:
加载动画效果演示

问题原因

闪烁的核心诱因有两个:

  1. CAShapeLayer的strokeStart、strokeEnd属性的合法取值范围为0~1,传入超出范围的值时系统会自动钳制到边界。你初始设置strokeStart的起始值为-0.1,在每轮动画重置的瞬间,strokeStart会被钳制为0,和同时重置为0的strokeEnd相等,描边长度瞬间变为0,产生肉眼可见的闪烁。
  2. 你没有给动画设置isRemovedOnCompletion = false和fillMode = .forwards,动画每轮结束后会自动跳回layer的model层对应值,和presentation层的展示值不一致也会导致跳闪。

修复代码

直接替换对应方法即可实现流畅无闪烁的加载动画:

func spin() {
    let circlePathLayer = CAShapeLayer()
    circlePathLayer.fillColor = nil
    circlePathLayer.strokeColor = UIColor.red.cgColor
    circlePathLayer.lineWidth = 6
    layer.addSublayer(circlePathLayer)
    setPath(to: circlePathLayer)
    animate(layer: circlePathLayer)
}

private func setPath(to layer: CAShapeLayer) {
    // 改为从顶部开始的圆形路径,符合常规加载动画的视觉习惯
    layer.path = UIBezierPath(arcCenter: CGPoint(x: bounds.midX, y: bounds.midY),
                              radius: (bounds.width - layer.lineWidth)/2,
                              startAngle: -CGFloat.pi / 2,
                              endAngle: 3 * CGFloat.pi / 2,
                              clockwise: true).cgPath
}

private func animate(layer: CAShapeLayer) {
    // 先给model层赋值,避免动画结束后跳变
    layer.strokeStart = 0.9
    layer.strokeEnd = 1.0
    
    animateKeyPath(keyPath: "strokeEnd",
                   duration: 1,
                   values: [0.1, 1],
                   layer: layer)
    animateKeyPath(keyPath: "strokeStart",
                   duration: 1,
                   values: [0, 0.9],
                   layer: layer)
    
    // 可选:添加旋转动画,实现更自然的转圈效果
    let rotateAnim = CABasicAnimation(keyPath: "transform.rotation")
    rotateAnim.fromValue = 0
    rotateAnim.toValue = CGFloat.pi * 2
    rotateAnim.duration = 2
    rotateAnim.repeatCount = .infinity
    layer.add(rotateAnim, forKey: "rotate")
}

private func animateKeyPath(keyPath: String, duration: CFTimeInterval, values: [CGFloat], layer: CAShapeLayer) {
    let animation = CAKeyframeAnimation(keyPath: keyPath)
    animation.values = values
    animation.calculationMode = .linear
    animation.duration = duration
    animation.repeatCount = .infinity
    // 新增动画配置,避免自动移除和跳变
    animation.isRemovedOnCompletion = false
    animation.fillMode = .forwards
    layer.add(animation, forKey: animation.keyPath)
}

内容的提问来源于stack exchange,提问作者iOS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 20:48:04