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

iOS开发:删除环形进度条动画后无法重新添加动画该如何解决?

故障原因
  • 核心问题是CAShapeLayer的模型层(Model Layer)属性和呈现层(Presentation Layer)属性不一致导致的:
    动画中设置的isRemovedOnCompletion = false和fillMode = .forwards仅让呈现层停在了动画终点strokeEnd = 1的状态,从未修改过模型层的strokeEnd值,它从draw(_ rect: CGRect)调用后一直保持为初始值0。
  • 调用removeAllAnimations()后,呈现层会立刻回退到模型层的属性值,但新旧动画切换在同一个RunLoop周期内,新动画的起始值计算会出现异常,导致动画无法正常触发。
  • 额外问题:draw(_ rect: CGRect)方法中不应该存放progressLayer.strokeEnd = 0这类属性赋值逻辑,draw方法仅应负责路径、绘制相关的核心逻辑,非绘制相关的属性赋值放在这里会导致意外的属性重置。
修复方案

修改CircularProgressBar类代码即可,ViewController无需改动,修改后的完整代码如下:

@IBDesignable
class CircularProgressBar: UIView {
    
    private var progressLayer = CAShapeLayer()
    private var backgroundMask = CAShapeLayer()
    
    @IBInspectable
    var ringWidth: CGFloat = 15 {
        didSet {
            backgroundMask.lineWidth = ringWidth
            progressLayer.lineWidth = ringWidth
            setNeedsDisplay()
        }
    }
    
    @IBInspectable
     var color: UIColor? = .purple {
        didSet {
            setNeedsDisplay()
        }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupLayers()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupLayers()
    }
    
    private func setupLayers() {
        backgroundMask.lineWidth = ringWidth
        backgroundMask.fillColor = nil
        backgroundMask.strokeColor = UIColor.gray.cgColor
        layer.addSublayer(backgroundMask)
        
        progressLayer.lineWidth = ringWidth
        progressLayer.fillColor = nil
        progressLayer.strokeColor = UIColor.yellow.cgColor
        // 初始strokeEnd移到初始化方法设置
        progressLayer.strokeEnd = 0
        layer.addSublayer(progressLayer)
    }
    
    override func draw(_ rect: CGRect) {
        // draw方法仅负责路径计算,使用当前视图bounds中点避免布局偏移
        let circlePath = UIBezierPath(arcCenter: CGPoint(x: bounds.midX, y: bounds.midY), radius: 140, startAngle: -(.pi / 2), endAngle: (3 * .pi / 2), clockwise: true).cgPath
        backgroundMask.path = circlePath
        progressLayer.path = circlePath
    }
    
     func addAnimation() {
        let duration: TimeInterval = 10
        // 先重置模型层起始状态
        progressLayer.strokeEnd = 0
        // 提前设置模型层终点值,动画结束后自动停在终点
        progressLayer.strokeEnd = 1
        
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.fromValue = 0
        animation.toValue = 1
        animation.duration = duration
        progressLayer.add(animation, forKey: "strokeEndAnimation")
    }
       
    func restartCircularProgressBar() {
        progressLayer.removeAllAnimations()
        // 删除动画后显式重置模型层状态
        progressLayer.strokeEnd = 0
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 22:24:03