在同一个 UIView 中多次点击手势检测

Posted

技术标签:

【中文标题】在同一个 UIView 中多次点击手势检测【英文标题】:Tap Gesture Detected Multiple Times in the same UIView 【发布时间】:2021-08-28 19:10:13 【问题描述】:

目前,我正在尝试将 UITapGestureRecognizer 添加到 UIView 以启动计时器。但是,每当我错误地多次点击UIView 时,都会识别出多个手势,并且计时器会运行两次,或者比平时快几倍。

我想确保UIView 仅识别 1 个计时器动作/1 个轻按手势,并且第二次轻按将是多余的(稍后我将努力确保第 2 次轻按“停止”计时器)。

我尝试阅读this answer,但它并没有完全指导我如何防止第二次点击或根据状态自定义操作,我仍在努力解决这个问题,但我陷入了困境问题。

如果您对我如何解决此问题有任何见解,请提供帮助。

class ActiveExerciseTableViewCell: UITableViewCell, UITextFieldDelegate 
    
    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) 
        restTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(step), userInfo: nil, repeats: true)
    
    
    @IBAction func pauseTapped(_ sender: Any) 
        restTimer.invalidate()
    
    
    @IBAction func resetTapped(_ sender: Any) 
        restTimer.invalidate()
        restTimeRemaining = 180
        timerLabel.text = "\(restTimeRemaining)"
    
    
    @objc func step() 
        if restTimeRemaining > 0 
            restTimeRemaining -= 1
         else 
            restTimer.invalidate()
            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)
    

【问题讨论】:

有点不相关,但我建议不要在ActiveExerciseTableViewCell 中使用Timer 之类的东西。当你滚动时这些会被重用,所以你以后可能会遇到一些奇怪的错误。最好在你的父视图控制器中管理它。 这是一个非常有用的信息。现在,我没有任何错误或问题。但是我确实理解您的观点,因为当我尝试实现键盘设置时遇到了类似的问题。最后,我不得不使用我的父视图。问题是 - 我还在学习,我不太清楚如何将 IBactions & objc func 连接到“UITableViewCell”类中的 UIcontrols。也许,我稍后会发布另一个问题。 您好 Aheze,我遇到了您提到的确切问题并发布了这个问题 - ***.com/questions/68975261/… 如果可以的话,我将不胜感激。我尝试将代码写入父控制器,但不知道如何通过“playTapped”等objective c方法传递“UILabel参数”,发现这样做很复杂。 【参考方案1】:

使用布尔值来处理状态变化:

class ActiveExerciseTableViewCell: UITableViewCell, UITextFieldDelegate 
    
    var restTimer = Timer()
    var restTimeRemaining: Int = 180
    var timerInitiated: Bool = false /// here!
    
    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) 
        if !timerInitiated  /// check here
            restTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(step), userInfo: nil, repeats: true)
            self.timerInitiated = true
        
    
    
    @IBAction func pauseTapped(_ sender: Any) 
        restTimer.invalidate()
    
    
    @IBAction func resetTapped(_ sender: Any) 
        restTimer.invalidate()
        restTimeRemaining = 180
        timerLabel.text = "\(restTimeRemaining)"
    
    
    @objc func step() 
        if restTimeRemaining > 0 
            restTimeRemaining -= 1
         else 
            restTimer.invalidate()
            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)
    

如果timerInitiated 布尔值为真,则表示练习已经开始,除非布尔值改变,否则不会再安排任何计时器。

【讨论】:

感谢您的回答:) 简单而优雅的解决方案

以上是关于在同一个 UIView 中多次点击手势检测的主要内容,如果未能解决你的问题,请参考以下文章

iOS - 多次点击手势识别器

点击手势事件在 UIView 上不起作用

在主uiview的子视图上获取点击事件

如何检测手指移入或移出我的自定义 UIView

创建一个捕捉点击但对所有其他手势透明的 UIView

如何以编程方式触发 UIView 的点击手势识别器