iOS双Timer联动异常:UI更新Timer滞后倒计时Timer1秒
解决两个Timer联动时UI更新滞后1秒的问题
我来帮你分析问题根源,再给出几个实用的解决方案:
问题核心原因
你的TimerModel倒计时逻辑顺序搞反了,再加上初始化时的同步缺失,直接导致UI永远慢一拍:
- 在
UpdateTime方法里,你先把当前timerCount转成字符串赋值给timerStr,再将timerCount减1——这意味着UI拿到的始终是上一秒的剩余时间。 - 调用
StartTimer后,第一个倒计时Timer要等1秒才会触发更新,而ViewController在viewDidLoad时取的timerStr还是初始值,直到1秒后才会拿到第一次更新的数值,视觉上就呈现出整整1秒的滞后。 - 用两个独立Timer同步数据的方式本身就容易因为RunLoop调度差异出现偏差,而且高频刷新UI完全是无效操作。
解决方案1:修正逻辑+通知驱动UI更新
先调整TimerModel的核心逻辑,确保timerStr和剩余时间完全同步,再用通知来触发UI更新(避免无效刷新):
修改TimerModel代码
class TimerModel: NSObject, NSCoding { var myTimer: Timer? var timerInterval: TimeInterval = 1.0 var timerEnd: TimeInterval = 0.0 var timerCount: TimeInterval = 86400.0 // 24 hours var timerStr: String = "TIME" func startTimer(time: Double) { timerCount = time // 初始化时先同步一次时间字符串,不用等第一个Timer触发 updateTimerString() // 先销毁旧Timer,避免重复创建导致的异常 myTimer?.invalidate() myTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateCountdown), userInfo: nil, repeats: true) } @objc private func updateCountdown() { timerCount -= 1 updateTimerString() print(self.timerStr) // 可选:添加倒计时结束的停止逻辑 if timerCount <= timerEnd { myTimer?.invalidate() myTimer = nil } } // 抽离字符串格式化逻辑,方便复用和维护 private func updateTimerString() { self.timerStr = self.formatTime(time: self.timerCount) // 发送通知通知UI更新 NotificationCenter.default.post(name: NSNotification.Name("CountdownUpdated"), object: nil) } private func formatTime(time:TimeInterval) -> String { let hours = Int(time) / 3600 let minutes = Int(time) / 60 % 60 let seconds = Int(time) % 60 return String(format: "%02i:%02i:%02i", hours, minutes, seconds) } }
修改ViewController代码
class HomeViewController: UIViewController { @IBOutlet var timeLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() // 初始显示当前倒计时 timeLabel.text = account.deedManager.globalTimer.timerStr // 注册通知接收倒计时更新 NotificationCenter.default.addObserver(self, selector: #selector(updateTimeLabel), name: NSNotification.Name("CountdownUpdated"), object: nil) } @objc private func updateTimeLabel() { self.timeLabel.text = account.deedManager.globalTimer.timerStr } deinit { // 记得移除通知,避免内存泄漏 NotificationCenter.default.removeObserver(self) } }
解决方案2:用闭包回调替代通知(更轻量)
如果觉得通知机制太重,可以在TimerModel里添加闭包回调,让ViewController直接订阅更新:
修改TimerModel代码
class TimerModel: NSObject, NSCoding { // 新增倒计时更新回调 var onCountdownUpdate: ((String) -> Void)? // ... 其他代码和方案1一致 ... private func updateTimerString() { self.timerStr = self.formatTime(time: self.timerCount) // 触发回调,通知外部更新 onCountdownUpdate?(self.timerStr) } }
修改ViewController代码
class HomeViewController: UIViewController { @IBOutlet var timeLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() let globalTimer = account.deedManager.globalTimer // 初始显示 timeLabel.text = globalTimer.timerStr // 设置回调,弱引用self避免内存泄漏 globalTimer.onCountdownUpdate = { [weak self] timeStr in // 确保UI更新在主线程执行 DispatchQueue.main.async { self?.timeLabel.text = timeStr } } } }
为什么之前的高频Timer没用?
你把UI更新Timer设为0.05秒,但TimerModel里的timerStr本身就是滞后1秒的错误值,所以不管UI刷新多频繁,拿到的都是旧数据,自然还是会慢1秒——修正倒计时逻辑才是解决问题的核心。
内容的提问来源于stack exchange,提问作者Klaviam
相关产品推荐
相关产品推荐

