Swift后台计时器异常求助:时分秒显示错误(如00:01:75)
解决Timer应用后台返回后时间显示异常的问题
嘿,我来帮你搞定这个计时器的bug!你遇到的00:01:75这种异常显示,核心问题是时间进位逻辑缺失,咱们一步步拆解修复:
问题根源拆解
- 后台恢复时的时间差处理错误:
getTimeDifference返回的秒数可能大于59(比如后台待了75秒就会返回75),你直接把这个数值硬加到self.sec里,没把超过60的部分转换成分钟,自然会出现秒数溢出。 - 前台计时的更新逻辑有漏洞:
updateLabels里只在sec == 59时才触发进位,这只适合每秒加1的常规场景,但如果sec初始值就大于59(比如后台回来后是75),这个判断根本不会触发,秒数会持续往上堆。
修复方案
第一步:重构时间差计算逻辑
放弃单独相加时分秒的方式,改用总秒数计算,再统一拆分成时、分、秒,自动处理进位。
第二步:修正前台计时更新逻辑
把原来的条件判断改成每次加1秒后,统一检查秒、分的进位(用>=60替代==59,逻辑更健壮)。
修正后的完整代码
override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(pauseWhenBackground(noti:)), name: UIApplication.didEnterBackgroundNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground(noti:)), name: UIApplication.willEnterForegroundNotification, object: nil) } @objc func willEnterForeground(noti: Notification) { guard let savedDate = UserDefaults.standard.object(forKey: "savedTime") as? Date else { return } // 计算后台停留的总秒数,替代拆分时分秒的方式 let totalElapsedSeconds = Int(Date().timeIntervalSince(savedDate)) // 用总秒数更新计时器变量,自动处理进位 updateTimerWithTotalSeconds(totalElapsedSeconds) // 重启前台计时器 self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(AddWorkTime.updateLabels(t:))), userInfo: nil, repeats: true) } // 新增:通过总秒数更新时分秒,自动处理进位逻辑 private func updateTimerWithTotalSeconds(_ addedSeconds: Int) { // 计算当前计时器的总秒数 let currentTotalSeconds = self.hrs * 3600 + self.min * 60 + self.sec let newTotalSeconds = currentTotalSeconds + addedSeconds // 拆分总秒数为时分秒 self.hrs = newTotalSeconds / 3600 self.min = (newTotalSeconds % 3600) / 60 self.sec = newTotalSeconds % 60 // 更新UI显示 self.time.text = String(format: "%02d : %02d : %02d", self.hrs, self.min, self.sec) } @objc func updateLabels(t: Timer) { // 先加1秒,再统一处理进位 self.sec += 1 if self.sec >= 60 { self.sec -= 60 self.min += 1 if self.min >= 60 { self.min -= 60 self.hrs += 1 } } self.time.text = String(format: "%02d : %02d : %02d", self.hrs, self.min, self.sec) } @objc func pauseWhenBackground(noti: Notification) { self.timer.invalidate() UserDefaults.standard.set(Date(), forKey: "savedTime") } func resetContent() { removeSavedDate() timer?.invalidate() self.time.text = "00 : 00 : 00" self.sec = 0 self.min = 0 self.hrs = 0 } func removeSavedDate() { UserDefaults.standard.removeObject(forKey: "savedTime") }
关键修改说明
- 总秒数计算更可靠:用
timeIntervalSince直接获取后台停留的秒数差,转换成整数后和当前计时器总秒数相加,再拆分成时分秒,彻底避免了直接相加时分秒的进位漏洞。 - 更新逻辑简化健壮:
updateLabels改成先加1秒,再检查秒数是否≥60,满足条件就减60并加1分钟;同理检查分钟数是否≥60,不管秒数初始值是多少,都能正确进位。 - 代码安全优化:加了
guard let判断savedDate,避免强制解包崩溃;timer?.invalidate()用可选链调用,防止timer为nil时崩溃。
这样修改后,不管后台待多久,返回前台后计时器都会正确显示时间,再也不会出现秒数超过60的异常啦!
内容的提问来源于stack exchange,提问作者ridvan
相关产品推荐
相关产品推荐

