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

如何仅通过CALayer.speed快速完成正在运行的进度条动画?

解决进度条动画提前完成时的快进问题
  • 你用CALayer.bounds从左到右的动画实现自定义进度条(类似系统进度条),当任务提前完成时需要将动画快进到终点。当前替换缩短时长动画副本的方案可行,但样板代码冗余,想通过CALayer.speed实现却遇到进度图层直接消失的异常。

  • 直接调整CALayer.speed导致图层消失的核心原因:如果动画未设置fillMode = .forwards和isRemovedOnCompletion = false,当动画时间进度超过1.0时,Core Animation会认为动画已结束并移除它,图层会跳回初始的bounds状态;另外,未精确计算剩余动画时间就盲目提速,也会导致时间逻辑混乱,触发异常。

用CALayer.speed实现快进的正确步骤

  1. 确保动画保留最终状态:添加进度条动画时,必须设置fillMode = .forwards和isRemovedOnCompletion = false,让动画结束后图层停在最终的进度状态。
  2. 精确计算并调整速度:
    • 获取动画已运行时长:通过layer.convertTime(CACurrentMediaTime(), from: nil) - animation.beginTime计算。
    • 计算剩余动画时长,再根据目标快进时长(比如0.1秒)算出需要的速度:speed = 剩余时长 / 目标快进时长。
  3. 动画完成后重置速度:监听动画完成事件,回调中将layer.speed恢复为1.0,避免影响后续动画逻辑。

示例代码

// 假设progressLayer是你的进度条图层,"boundsAnimation"是动画的key
func fastForwardProgressAnimation() {
    guard let runningAnimation = progressLayer.animation(forKey: "boundsAnimation") else { return }
    
    // 确保动画结束后保留最终状态(若之前未设置)
    runningAnimation.fillMode = .forwards
    runningAnimation.isRemovedOnCompletion = false
    
    // 计算已运行时长与剩余时长
    let currentMediaTime = progressLayer.convertTime(CACurrentMediaTime(), from: nil)
    let elapsedTime = currentMediaTime - runningAnimation.beginTime
    let remainingTime = runningAnimation.duration - elapsedTime
    
    guard remainingTime > 0 else { return }
    
    // 设置速度,让剩余动画在0.1秒内完成
    let targetFastForwardDuration: CFTimeInterval = 0.1
    progressLayer.speed = remainingTime / targetFastForwardDuration
    
    // 动画完成后重置speed
    CATransaction.begin()
    CATransaction.setCompletionBlock {
        self.progressLayer.speed = 1.0
    }
    CATransaction.commit()
}

补充说明

苹果文档里的“暂停后以更高速度恢复”方法,未针对进度条这种需要严格保留最终状态的场景做适配,直接套用会忽略fillMode和时间精度的问题,才导致图层消失。上面的方案通过补全动画状态配置、精确计算速度,就能用CALayer.speed实现无异常的快进效果,大幅减少样板代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 19:58:19