如何保存UIProgressView动画状态并恢复?中途重置动画怎么做?
问题1:后台暂停后前台恢复动画消失的解决方法
当APP进入后台时,系统会暂停甚至移除UIKit动画,仅靠操作layer的timeOffset和speed无法恢复已被系统清理的动画。正确的做法是保存当前动画的进度状态,回到前台时基于保存的进度重新创建动画。
实现步骤:
- 添加变量保存当前进度和剩余动画时长:
private var currentProgress: Float = 0 private var remainingAnimationDuration: TimeInterval? private let totalAnimationDuration: TimeInterval = 5 // 与启动方法的总时长保持一致
- 修改暂停方法,保存当前显示的进度和剩余时长:
func pauseProgressAnimation() { // 获取动画实时显示的进度(presentationLayer对应当前屏幕上的视觉状态) guard let presentationLayer = progressView.layer.presentation() as? UIProgressViewLayer else { return } currentProgress = presentationLayer.progress // 计算剩余动画时长 let elapsedRatio = TimeInterval(currentProgress) remainingAnimationDuration = totalAnimationDuration * (1 - elapsedRatio) // 清除当前动画并重置layer状态 progressView.layer.removeAllAnimations() progressView.layer.speed = 1 progressView.layer.timeOffset = 0 progressView.layer.beginTime = 0 // 将视图进度同步到当前保存的进度值 progressView.setProgress(currentProgress, animated: false) }
- 修改恢复方法,基于保存的进度重新启动动画:
func resumeProgressAnimation() { guard let remainingDuration = remainingAnimationDuration, currentProgress < 1 else { // 若进度已完成或无剩余时长,重新启动完整动画 startProgressAnimation() return } // 从保存的进度点开始剩余时长的动画 progressView.setProgress(currentProgress, animated: false) UIView.animate(withDuration: remainingDuration, options: .curveLinear) { self.progressView.setProgress(1, animated: true) } completion: { [weak self] finished in if finished { self?.delegate?.scrollToNextStory() } } }
- 优化启动方法,支持从指定进度开始:
func startProgressAnimation(fromProgress: Float = 0) { currentProgress = fromProgress remainingAnimationDuration = totalAnimationDuration * TimeInterval(1 - fromProgress) progressView.setProgress(fromProgress, animated: false) UIView.animate(withDuration: remainingAnimationDuration!, delay: 0, options: .curveLinear) { self.progressView.setProgress(1, animated: true) } completion: { [weak self] finished in if finished { self?.delegate?.scrollToNextStory() } } }
问题2:动画中途重置并重新播放的正确实现
你当前的resetProgress方法思路正确,但可以简化优化,确保彻底清除动画状态并重置进度:
优化后的重置方法:
func resetProgress() { // 清除所有动画(包括子层的进度动画) progressView.layer.removeAllAnimations() // 重置layer时间相关属性,避免残留状态影响后续动画 progressView.layer.speed = 1 progressView.layer.timeOffset = 0 progressView.layer.beginTime = 0 // 无动画地将进度重置到初始状态 progressView.setProgress(0, animated: false) // 若需要重置后立即重新播放,取消下面的注释 // startProgressAnimation() }
说明:
- 无需遍历子层移除动画,
progressView.layer.removeAllAnimations()会自动清除所有子层的动画。 - 重置layer的
speed、timeOffset、beginTime是为了避免之前的暂停/恢复操作残留状态,导致后续动画异常。 - 如果需要重置后立即启动动画,直接调用
startProgressAnimation()即可。
内容的提问来源于stack exchange,提问作者Alisher Baigazin
相关产品推荐
相关产品推荐

