同一UIView中UITapGestureRecognizer多次触发导致定时器异常如何解决
问题根因
每次点击UIView都会创建新的Timer实例,旧定时器未被销毁,多个定时器同时触发倒计时逻辑,导致运行速度远超预期。
修复方案
只需新增定时器运行状态判断逻辑,即可阻止重复启动:
- 将
restTimer声明为可选类型,用来标记当前是否存在正在运行的定时器 - 点击启动逻辑前先校验定时器状态,若已在运行则直接拦截本次点击
- 在定时器暂停、重置、自然结束的节点将
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
相关产品推荐
相关产品推荐

