UIProgressView点击按钮后动画无法重启问题求助
解决UIProgressView首次点击正常,后续动画无法重启的问题
嘿,我看了你的代码,问题主要出在动画的异步执行顺序和未正确管理定时器这两个点上,咱们一步步来解决:
问题根源分析
- 重置动画与启动动画的冲突:你调用
resetProgress()时用了animated: true,这个动画是异步的——函数执行完返回后,进度条还在慢慢往0走。这时候你立刻调用startProgressView去把进度拉到1,系统会把这两个动画合并,结果就是你看不到重置后的动画效果,直接跳转到新的动画状态。 - 多定时器冲突:每次点击按钮都会创建一个新的
Timer,之前的定时器如果没被销毁,会继续在后台运行,不仅浪费资源,还可能干扰进度条的状态逻辑。 - 双重动画嵌套:在
UIView.animate闭包里调用progressView.setProgress(1.0, animated: true),等于同时触发了UIView的帧动画和progressView自身的进度动画,两者叠加会导致动画异常。
修复后的完整代码
import UIKit class ViewController: UIViewController { let eggTimes = ["Soft" : 5, "Medium" : 7, "Hard": 12] @IBOutlet weak var progressView: UIProgressView! // 新增属性保存当前定时器,方便管理 var currentTimer: Timer? // 重置进度条:取消所有动画,立刻回到0 func resetProgress(){ progressView.layer.removeAllAnimations() progressView.setProgress(0, animated: false) print("function progress view reset fired") } // 启动进度条动画:用UIView的动画统一控制,避免双重动画 func startProgressView(duration: Double) { progressView.layer.removeAllAnimations() UIView.animate(withDuration: duration) { print("progress bar animation started") self.progressView.setProgress(1.0, animated: false) } } @IBAction func hardnessSelected(_ sender: UIButton) { // 第一步:先停止之前的定时器,避免多个定时器同时运行 currentTimer?.invalidate() // 第二步:重置进度条 resetProgress() print("progress bar has been reset") // 第三步:获取选中的硬度对应的时长 let hardness = sender.currentTitle! let totalSeconds = Double(eggTimes[hardness]!) * 10 print("total duration: \(totalSeconds) ") // 第四步:启动进度动画 self.startProgressView(duration: totalSeconds) // 第五步:启动新的定时器 var timerCounter = eggTimes[hardness]! * 10 currentTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] timer in guard let self = self else { timer.invalidate() return } if timerCounter > 0 { timerCounter -= 1 } else { print("egg ready") timer.invalidate() self.currentTimer = nil // 清空定时器引用 } } } }
关键修复点说明
- 强制终止所有旧动画:每次重置或启动新动画前,调用
progressView.layer.removeAllAnimations(),确保之前的动画完全停止,不会和新动画冲突。 - 同步重置进度:重置进度条时用
animated: false,让进度条立刻回到0,而不是等待异步动画完成,这样后续的新动画能从正确的起点开始。 - 统一动画控制:在
UIView.animate闭包里调用setProgress时用animated: false,让UIView的动画统一管理进度变化的过渡效果,避免双重动画导致的异常。 - 定时器管理:用
currentTimer属性保存当前定时器,每次点击先销毁旧定时器,再创建新的,避免多个定时器同时运行。 - 弱引用避免内存泄漏:在Timer的闭包里用
[weak self],防止ViewController被定时器强引用导致内存泄漏。
这样修改后,不管你点击多少次按钮,进度条都会先立刻重置为0,然后正常启动新的进度动画,定时器也不会出现多个同时运行的问题啦~
内容的提问来源于stack exchange,提问作者Meir
相关产品推荐
相关产品推荐

