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

iOS双Timer联动异常:UI更新Timer滞后倒计时Timer1秒

解决两个Timer联动时UI更新滞后1秒的问题

我来帮你分析问题根源,再给出几个实用的解决方案:

问题核心原因

你的TimerModel倒计时逻辑顺序搞反了,再加上初始化时的同步缺失,直接导致UI永远慢一拍:

  1. 在UpdateTime方法里,你先把当前timerCount转成字符串赋值给timerStr,再将timerCount减1——这意味着UI拿到的始终是上一秒的剩余时间。
  2. 调用StartTimer后,第一个倒计时Timer要等1秒才会触发更新,而ViewController在viewDidLoad时取的timerStr还是初始值,直到1秒后才会拿到第一次更新的数值,视觉上就呈现出整整1秒的滞后。
  3. 用两个独立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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:19:52