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

同一UIView中UITapGestureRecognizer多次触发导致定时器异常如何解决

问题根因

每次点击UIView都会创建新的Timer实例,旧定时器未被销毁,多个定时器同时触发倒计时逻辑,导致运行速度远超预期。

修复方案

只需新增定时器运行状态判断逻辑,即可阻止重复启动:

  1. 将restTimer声明为可选类型,用来标记当前是否存在正在运行的定时器
  2. 点击启动逻辑前先校验定时器状态,若已在运行则直接拦截本次点击
  3. 在定时器暂停、重置、自然结束的节点将restTimer置空,恢复可再次启动的状态

修改后的完整代码如下:

class ActiveExerciseTableViewCell: UITableViewCell, UITextFieldDelegate {
    
    // 改为可选类型,nil代表当前无运行中的定时器
    var restTimer: Timer?
    var restTimeRemaining: Int = 180
    
    func setUpActiveExerciseUIViewLayout(){
        
        timerLabel.translatesAutoresizingMaskIntoConstraints = false
        timerLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        timerLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: (contentView.frame.height-tableviewContentViewTabBarHeight)*0.55).isActive = true
        timerLabel.widthAnchor.constraint(equalToConstant: contentView.frame.width * 0.7).isActive = true
        timerLabel.heightAnchor.constraint(equalToConstant: 80).isActive = true
        timerLabel.font = .boldSystemFont(ofSize: 64)
        
        
        activeExerciseTimerUIView.translatesAutoresizingMaskIntoConstraints = false
        activeExerciseTimerUIView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
        activeExerciseTimerUIView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: (contentView.frame.height-tableviewContentViewTabBarHeight)*0.25).isActive = true
        activeExerciseTimerUIView.widthAnchor.constraint(equalToConstant: 225).isActive = true
        activeExerciseTimerUIView.heightAnchor.constraint(equalToConstant: 225).isActive = true
        
        
        let timerStartGesture = UITapGestureRecognizer(target: self, action: #selector(playTapped))
        timerStartGesture.numberOfTapsRequired = 1
        
        activeExerciseTimerUIView.addGestureRecognizer(timerStartGesture)
        activeExerciseTimerUIView.isUserInteractionEnabled = true
    }
    
    @objc func playTapped(_ sender: Any) {
        // 已存在运行中的定时器,直接拦截
        guard restTimer == nil else {
            return
        }
        restTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(step), userInfo: nil, repeats: true)
    }
    
    @IBAction func pauseTapped(_ sender: Any) {
        restTimer?.invalidate()
        // 暂停后置空,允许下次点击启动
        restTimer = nil
    }
    
    @IBAction func resetTapped(_ sender: Any) {
        restTimer?.invalidate()
        // 重置后置空,允许下次点击启动
        restTimer = nil
        restTimeRemaining = 180
        timerLabel.text = prodTimeString(time: TimeInterval(restTimeRemaining))
    }
    
    @objc func step() {
        if restTimeRemaining > 0 {
            restTimeRemaining -= 1
        } else {
            restTimer?.invalidate()
            // 倒计时自然结束后置空,允许下次点击启动
            restTimer = nil
            restTimeRemaining = 180
        }
        timerLabel.text = prodTimeString(time: TimeInterval(restTimeRemaining))
    }
    
    func prodTimeString(time: TimeInterval) -> String {
        let Minutes = Int(time) / 60 % 60
        let Seconds = Int(time) % 60
        
        return String(format: "%02d:%02d", Minutes, Seconds)
    }
}

后续如果需要实现点击暂停功能,只需要修改playTapped中guard判断分支的逻辑即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 16:15:02