【问题标题】:UITapGestureRecognizer tap on self.view but ignore subviewsUITapGestureRecognizer 点击 self.view 但忽略子视图
【发布时间】:2013-03-26 17:36:03
【问题描述】:

我需要实现一个功能,当我双击 self.view(UIViewCotroller 的视图)时会调用一些代码。但问题是我在这个视图上有其他 UI 对象,我不想将任何识别器对象附加到所有这些对象上。我在下面找到了如何在我的视图上做手势的方法,我知道它是如何工作的。现在我在障碍面前选择哪种方式来创建这个识别器忽略子视图。有任何想法吗?谢谢。

UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
[doubleTap setNumberOfTapsRequired:2];
[self.view addGestureRecognizer:doubleTap];

【问题讨论】:

  • 我不确定,但您是否尝试在识别器上将 cancelsTouchesInView 设置为 NO?所以 [doubleTap setCancelsTouchesInView:NO];

标签: ios uitapgesturerecognizer


【解决方案1】:

您应该在self 对象内部采用UIGestureRecognizerDelegate 协议并调用以下方法来检查视图。在此方法中,对照touch.view 检查您的视图并返回适当的布尔值(是/否)。像这样的:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([touch.view isDescendantOfView:yourSubView]) {
        return NO;
    }
    return YES;
}

编辑:请同时检查@Ian 的回答!

斯威夫特 5

// MARK: UIGestureRecognizerDelegate methods, You need to set the delegate of the recognizer
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
     if touch.view?.isDescendant(of: tableView) == true {
        return false
     }
     return true
}

【讨论】:

  • 还有一个问题,我的视图中有几个按钮必须起作用。我怎样才能为他们设置一些优先级?
  • 如果你的按钮有自己的手势识别器(很可能),挖掘它们的内部并抓住它们,并阅读-[UIGestureRecognizer requireGestureRecognizerToFail:] 上的文档,但它可能在不修改它们的情况下工作
  • 找了好几个小时了!谢谢! ;)
  • 如果您的 if 测试失败,您的实现将无法返回 BOOL;在 if 块(使用 { })或 else 分支后,正确的样式返回 YES。不过,谢谢,为我节省了一些阅读时间。
  • 视图是其自身的后代,因此这不起作用...(不过总体方法还可以,请参阅其他答案)
【解决方案2】:

另一种方法是仅比较触摸视图是否为手势视图,因为后代不会通过条件。一个漂亮、简单的单行:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view == gestureRecognizer.view
}

【讨论】:

  • 这对我来说比接受的答案更好。更简洁。
  • @Ian 在点击视图时不会调用此方法。
  • 非常简洁的答案!
  • 接受的答案甚至没有用,但这对我来说完美无缺。
  • @Prabu 可能是因为手势识别器的代理必须在之前设置
【解决方案3】:

对于 Swift 变体:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if touch.view.isDescendantOfView(yourSubView){
        return false
    }
    return true
}

很高兴知道,isDescendantOfView 返回一个Boolean 值,指示接收者是给定视图的子视图还是与该视图相同。

【讨论】:

  • 别忘了在你的课堂上设置 UIGestureRecognizerDelegate !
  • 不做那个 if 语句,你不能直接返回这个吗? return !touch.view.isDescendant(of:gestureRecognizer.view) 另外,swift 3 中的新语法 ^^
【解决方案4】:

在 Swift 5 和 iOS 12 中,UIGestureRecognizerDelegate 有一个名为 gestureRecognizer(_:shouldReceive:) 的方法。 gestureRecognizer(_:shouldReceive:) 有以下声明:

询问代表手势识别器是否应该接收代表触摸的对象。

optional func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool

下面的完整代码显示了gestureRecognizer(_:shouldReceive:) 的可能实现。使用此代码,点击ViewController 视图的子视图(包括imageView)不会触发printHello(_:) 方法。

import UIKit

class ViewController: UIViewController, UIGestureRecognizerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(printHello))
        tapGestureRecognizer.delegate = self
        view.addGestureRecognizer(tapGestureRecognizer)

        let imageView = UIImageView(image: UIImage(named: "icon")!)
        imageView.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
        view.addSubview(imageView)

        // ⚠️ Enable user interaction for imageView so that it can participate to touch events.
        // Otherwise, taps on imageView will be forwarded to its superview and managed by it.
        imageView.isUserInteractionEnabled = true
    }

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        // Prevent subviews of a specific view to send touch events to the view's gesture recognizers.
        if let touchedView = touch.view, let gestureView = gestureRecognizer.view, touchedView.isDescendant(of: gestureView), touchedView !== gestureView {
            return false
        }
        return true
    }

    @objc func printHello(_ sender: UITapGestureRecognizer) {
        print("Hello")
    }

}

gestureRecognizer(_:shouldReceive:) 的替代实现可以是:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return gestureRecognizer.view === touch.view
}

但请注意,此替代代码不会检查 touch.view 是否是 gestureRecognizer.view 的子视图。

【讨论】:

    【解决方案5】:

    完整的快速解决方案(必须为识别器实施并设置委托):

    class MyViewController: UIViewController UIGestureRecognizerDelegate {
    
        override func viewDidLoad() {
            let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(onBaseTapOnly))
            doubleTapRecognizer.numberOfTapsRequired = 2
            doubleTapRecognizer.delegate = self
            self.view.addGestureRecognizer(doubleTapRecognizer)
        }
    
        func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
            if touch.view.isDescendantOfView(self.view){
                return false
            }
            return true
        }
    
        func onBaseTapOnly(sender: UITapGestureRecognizer) {
            if sender.state == .Ended {
                //react to tap
            }
        }
    }
    

    【讨论】:

      【解决方案6】:

      使用您触摸的 CGPoint 的变体 (SWIFT 4.0)

      class MyViewController: UIViewController, UIGestureRecognizerDelegate {
      
        func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
      
      // Get the location in CGPoint
          let location = touch.location(in: nil)
      
      // Check if location is inside the view to avoid
          if viewToAvoid.frame.contains(location) {
              return false
          }
      
          return true
        }
      }
      

      【讨论】:

        【解决方案7】:

        清除 Swift 方式

        func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
            return touch.view == self.view
        }
        

        【讨论】:

        • 永远不会在 iOS 13 中被调用,至少使用 swipeGestureRecognizer。
        • 这与伊恩的回答有何不同?
        【解决方案8】:

        请注意,gestureRecognizer API 已更改为:

        gestureRecognizer(_:shouldReceive:)

        请特别注意第一个参数的外部标签的下划线(跳过)指示符。

        使用上面提供的许多示例,我没有收到该事件。下面是一个适用于当前版本的 Swift (3+) 的示例。

        public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
            var shouldReceive = false
            if let clickedView = touch.view {
                if clickedView == self.view {
                    shouldReceive = true;
                }
            }
            return shouldReceive
        }
        

        【讨论】:

          【解决方案9】:

          加上以上解决方案,别忘了检查你的子视图的User Interaction Enabled

          【讨论】:

            【解决方案10】:

            我不得不阻止子视图上的手势。唯一有效的是允许并保留第一个视图并阻止所有下一个视图中的手势:

               var gestureView: UIView? = nil
            
                func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
                    if (gestureView == nil || gestureView == touch.view){
                        gestureView = touch.view
                        return true
                    }
                    return false
                 }
            

            【讨论】:

              【解决方案11】:

              斯威夫特 4:

              touch.view 现在是可选的,因此基于@Antoine 的回答:

              func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
                  if let touchedView = touch.view, touchedView.isDescendant(of: deductibleBackgroundView) {
                      return false
                  }
                  return true
              }
              

              【讨论】:

                【解决方案12】:

                如果您不希望您的“双击识别器”与您的按钮和/或其他控件发生冲突,您可以将 self 设置为 UIGestureRecognizerDelegate 并实现:

                func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
                {
                    return !(touch.view is UIControl)
                }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2016-11-08
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2013-11-04
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多