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

Swift 新增TableViewCell时原有Cell内DispatchSourceTimer秒数重置问题

问题原因分析

该问题是数据源设计、Cell复用逻辑、刷新逻辑共同导致的,核心原因如下:

  • 数据源MapTimer仅存储了倒计时的小时、分钟数值,没有存储固定的倒计时截止时间,每次调用setup方法时,都会基于当前时间重新计算expires,导致倒计时的秒数被重新计算,而小时、分钟因为和模型存储的数值一致,看起来没有变化。
  • 新增Cell时你应该是调用了全局的tableView.reloadData(),所有已展示的旧Cell都会重新执行setup方法,覆盖了原本的expires值,触发秒数重置。
  • Cell的setup方法执行时没有提前销毁旧的计时器,可能出现单个Cell绑定多个计时器同时回调的问题,也会引发时间跳变、资源泄漏。
修复方案

按以下步骤依次修改即可解决问题:

  1. 修改MapTimer模型,预存倒计时截止时间,不要在Cell层计算截止时间
class MapTimer {
    // 原有属性保留
    var hour: Int
    var min: Int
    // 新增截止时间戳属性,初始化时直接计算完成
    var expiresTimestamp: TimeInterval
    
    init(hour: Int, minute: Int) {
        self.hour = hour
        self.min = minute
        let addComponents = DateComponents(hour: hour, minute: minute)
        self.expiresTimestamp = Calendar.current.date(byAdding: addComponents, to: Date())!.timeIntervalSince1970
    }
}
  1. 优化Cell的setup方法,先清理旧计时器,直接使用模型预存的截止时间
func setup(with timer: MapTimer) {
    // 先销毁旧计时器,避免重复回调
    stopwatch?.cancel()
    stopwatch = nil
    // 直接使用模型存储的截止时间,不需要重新计算
    expires = Date(timeIntervalSince1970: timer.expiresTimestamp)
    
    stopwatch = DispatchSource.makeTimerSource(queue: .main)
    stopwatch?.schedule(deadline: .now(), repeating: 1.0)
    stopwatch?.setEventHandler { [weak self] in
        self?.countDownTimer()
    }
    stopwatch?.resume()
}
  1. 优化TableView刷新逻辑,新增Cell时不要全局刷新,只插入对应新行即可
// 新增计时器后不要调用reloadData(),改为插入指定行
tableView.insertRows(at: [IndexPath(row: 新行索引, section: 0)], with: .automatic)
  1. 补全秒数的两位格式化,避免秒数小于10时显示异常,同时添加Cell复用的清理逻辑
@objc fileprivate func countDownTimer() {
    guard let expires = expires else { return }
    let timeLeft = Calendar.current.dateComponents([.hour,.minute,.second], from: Date(), to: expires)
    guard let hour = timeLeft.hour, let minute = timeLeft.minute, let second = timeLeft.second else { return }
    
    let hourFormatted = hour > 9 ? "\(hour)" : "0\(hour)"
    let minFormatted = minute > 9 ? "\(minute)" : "0\(minute)"
    // 补全秒数格式化
    let secFormatted = second > 9 ? "\(second)" : "0\(second)"
    timerTime.text = "\(hourFormatted):\(minFormatted):\(secFormatted)"
    
    if hour == 0 && minute == 0 && second <= 29 {
        timerTime.textColor = UIColor(named: "Red") ?? .red
    } else {
        timerTime.textColor = .white
    }
    
    if hour == 0 && minute == 0 && second == 0 {
        stopwatch?.cancel()
        stopwatch = nil
    }
}

// 重写prepareForReuse,Cell复用时清理旧资源
override func prepareForReuse() {
    super.prepareForReuse()
    stopwatch?.cancel()
    stopwatch = nil
    timerTime.text = nil
    timerTime.textColor = .white
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 09:15:04