如何仅通过CALayer.speed快速完成正在运行的进度条动画?
解决进度条动画提前完成时的快进问题
你用
CALayer.bounds从左到右的动画实现自定义进度条(类似系统进度条),当任务提前完成时需要将动画快进到终点。当前替换缩短时长动画副本的方案可行,但样板代码冗余,想通过CALayer.speed实现却遇到进度图层直接消失的异常。直接调整
CALayer.speed导致图层消失的核心原因:如果动画未设置fillMode = .forwards和isRemovedOnCompletion = false,当动画时间进度超过1.0时,Core Animation会认为动画已结束并移除它,图层会跳回初始的bounds状态;另外,未精确计算剩余动画时间就盲目提速,也会导致时间逻辑混乱,触发异常。
用CALayer.speed实现快进的正确步骤
- 确保动画保留最终状态:添加进度条动画时,必须设置
fillMode = .forwards和isRemovedOnCompletion = false,让动画结束后图层停在最终的进度状态。 - 精确计算并调整速度:
- 获取动画已运行时长:通过
layer.convertTime(CACurrentMediaTime(), from: nil) - animation.beginTime计算。 - 计算剩余动画时长,再根据目标快进时长(比如0.1秒)算出需要的速度:
speed = 剩余时长 / 目标快进时长。
- 获取动画已运行时长:通过
- 动画完成后重置速度:监听动画完成事件,回调中将
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
相关产品推荐
相关产品推荐

