You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS倒计时计时器后台返回后无法运行的问题求助

解决iOS后台返回后计时器失效的问题

问题根源

你的代码存在两个核心问题:

  1. runTimer() 方法每次调用都会覆盖 UserDefaults 里的 lastTimerStartTime 和 totalTime,导致从后台返回时,原本的时间记录被当前时间和剩余秒数覆盖,计算逻辑完全错误。
  2. 没有处理APP前后台切换的事件,后台运行时系统会暂停 Timer,但你的代码没有在后台时保存正确的剩余时间,也没有在回到前台时基于后台停留时长更新剩余时间。

解决方案

通过监听APP前后台切换通知,在后台时暂停计时器并保存状态,回到前台时计算后台停留时长、更新剩余时间后重启计时器,同时修正计时器启动和存储的逻辑。

修改后的完整代码

import UIKit

class ViewController: UIViewController {
    var timer: Timer?
    var seconds: Double = 60 // 初始倒计时时长,可根据需求修改
    
    @IBOutlet weak var timeLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 注册前后台切换通知
        NotificationCenter.default.addObserver(self, selector: #selector(appDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
        
        // 初始化显示时间
        timeLabel.text = timeString(time: TimeInterval(seconds))
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // 从存储中恢复剩余时间并启动计时器(如果还有剩余)
        if let savedSeconds = UserDefaults.standard.value(forKey: "remainingSeconds") as? Double, savedSeconds > 0 {
            seconds = savedSeconds
            runTimer()
        }
    }
    
    @IBAction func startBtn(_ sender: Any) {
        // 避免重复启动计时器
        guard timer?.isValid != true else { return }
        // 重置为初始时长并启动
        seconds = 60
        runTimer()
    }
    
    func runTimer() {
        // 先销毁已有计时器,防止重复触发
        timer?.invalidate()
        timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
    }
    
    @objc func updateTimer() {
        seconds -= 1
        timeLabel.text = timeString(time: TimeInterval(seconds))
        
        // 倒计时结束后停止计时器并清理存储
        if seconds <= 0 {
            timer?.invalidate()
            UserDefaults.standard.removeObject(forKey: "remainingSeconds")
            UserDefaults.standard.removeObject(forKey: "backgroundEnterTime")
        }
    }
    
    @objc func appDidEnterBackground() {
        // 进入后台时停止计时器,保存当前状态
        timer?.invalidate()
        UserDefaults.standard.set(seconds, forKey: "remainingSeconds")
        UserDefaults.standard.set(Date(), forKey: "backgroundEnterTime")
    }
    
    @objc func appWillEnterForeground() {
        // 回到前台时计算后台停留时长,更新剩余时间
        guard let backgroundEnterTime = UserDefaults.standard.value(forKey: "backgroundEnterTime") as? Date,
              let savedRemainingSeconds = UserDefaults.standard.value(forKey: "remainingSeconds") as? Double else {
            return
        }
        
        let timeSpentInBackground = Date().timeIntervalSince(backgroundEnterTime)
        seconds = savedRemainingSeconds - timeSpentInBackground
        
        if seconds > 0 {
            runTimer()
            timeLabel.text = timeString(time: TimeInterval(seconds))
        } else {
            seconds = 0
            timeLabel.text = timeString(time: 0)
            UserDefaults.standard.removeObject(forKey: "remainingSeconds")
            UserDefaults.standard.removeObject(forKey: "backgroundEnterTime")
        }
    }
    
    // 时间格式化方法(假设你已有这个方法)
    func timeString(time: TimeInterval) -> String {
        let minutes = Int(time) / 60 % 60
        let seconds = Int(time) % 60
        return String(format:"%02i:%02i", minutes, seconds)
    }
    
    deinit {
        // 移除通知监听
        NotificationCenter.default.removeObserver(self)
    }
}

关键修改说明

  • 前后台通知监听:在viewDidLoad中注册didEnterBackground和willEnterForeground通知,分别处理后台暂停和前台恢复逻辑。
  • 后台状态保存:进入后台时立即停止计时器,保存当前剩余秒数和进入后台的时间戳。
  • 前台时间修正:回到前台时计算后台停留的总时长,从保存的剩余秒数中扣除,确保倒计时准确衔接。
  • 避免重复计时器:每次启动计时器前先销毁已有实例,防止多个计时器同时运行导致秒数跳变。
  • 倒计时结束清理:倒计时结束后清空存储的状态,避免下次进入APP时错误恢复。

内容的提问来源于stack exchange,提问作者Ricky Fernandez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 07:20:29