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

iOS开发:24小时促销计时器无法停止,代码调试求助

解决24小时促销计时器停止逻辑失效的问题

问题根源

  1. 错误的时间对比逻辑:每次调用updateCountDown都会重新生成newDate(当前时间+35秒),这个日期和你存在UserDefaults里的waitingDate完全无关,导致判断结束的逻辑根本没用到正确的截止时间。
  2. 时间差计算方向错误:Calendar.current.dateComponents([.second], from: newDate!, to: Date())计算的是当前时间比newDate晚多少秒,但newDate是当前时间+35秒,所以这个差值是负数,永远不可能大于35,自然不会进入停止分支。
  3. 未持有定时器实例:即使判断到倒计时结束,也没有办法停止定时器,它会继续每秒触发一次。

修改后的完整代码

class SLTimer {
    // 计算剩余时间,处理可选值并补零优化格式
    func timeLeftExtended(date: Date) -> String {
        let cal = Calendar.current
        let now = Date()
        let components = cal.dateComponents([.hour, .minute, .second], from: now, to: date)
        
        let hour = components.hour ?? 0
        let minute = components.minute ?? 0
        let second = components.second ?? 0
        
        return String(format: "%02d : %02d : %02d", hour, minute, second)
    }
    
    func updateCountDown() -> String? {
        guard let waitingDate = UserDefaults.standard.value(forKey: "waitingDate") as? Date else {
            // 首次启动设置截止时间(测试用35秒,正式环境改为86400秒即24小时)
            let endDate = Calendar.current.date(byAdding: .second, value: 35, to: Date())!
            UserDefaults.standard.set(endDate, forKey: "waitingDate")
            return timeLeftExtended(date: endDate)
        }
        
        let now = Date()
        // 直接判断当前时间是否超过截止时间
        if now >= waitingDate {
            UserDefaults.standard.removeObject(forKey: "waitingDate")
            return "stop"
        } else {
            return timeLeftExtended(date: waitingDate)
        }
    }
}

class ViewController: UIViewController {
    // 持有定时器实例,用于停止倒计时
    private var countdownTimer: Timer?
    private let slTimer = SLTimer()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        startCountdown()
    }
    
    private func startCountdown() {
        // 初始化重复定时器
        countdownTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateButtonTitle), userInfo: nil, repeats: true)
        // 立即更新按钮标题,避免延迟1秒显示
        updateButtonTitle()
    }
    
    @objc private func updateButtonTitle() {
        guard let timeString = slTimer.updateCountDown() else { return }
        
        if timeString == "stop" {
            saleButton.setTitle("促销结束", for: .normal)
            // 停止定时器并释放资源
            countdownTimer?.invalidate()
            countdownTimer = nil
        } else {
            saleButton.setTitle(timeString, for: .normal)
        }
    }
}

关键改动说明

  • SLTimer优化:
    • 仅在首次启动时设置并存储截止时间,后续统一使用该时间判断是否结束。
    • 直接对比当前时间和截止时间,逻辑更直观,避免时间差计算错误。
    • 处理可选值避免崩溃,同时用String(format:)给数字补零,让倒计时格式更规范(如00:00:05)。
  • ViewController优化:
    • 新增countdownTimer属性持有定时器实例,方便在倒计时结束时调用invalidate()停止。
    • 复用slTimer实例,避免每次创建新对象。
    • 启动时立即更新按钮标题,解决定时器第一次触发延迟1秒的问题。
    • 倒计时结束时,更新按钮为“促销结束”并停止定时器,避免无效触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 18:57:12