UITapGestureRecognizer点击self.view但忽略子视图


93

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

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

1
我不确定,但是您是否尝试过在识别器上将cancelsTouchesInView设置为NO?因此[doubleTap setCancelsTouchesInView:NO];
JDx

Answers:


142

您应该UIGestureRecognizerDelegateself对象内部采用协议,并调用以下方法检查视图。在此方法内,对照您的视图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
}

还有一个问题,我的视图上有几个按钮必须起作用。我该如何为他们设置一些优先级?
Matrosov Alexander

如果您的按钮有自己的手势识别器(可能),请深入了解其内部并抓取它们,然后阅读文档,-[UIGestureRecognizer requireGestureRecognizerToFail:]但可能无需修改即可起作用
bshirley 2013年

一直在寻找这个小时!谢谢!;)
MasterRazer 2013年

如果if测试失败,则实现无法返回BOOL;为了获得正确的样式,在if块之后(使用{ })或在else分支中返回YES 。不过,谢谢,为我节省了一些阅读时间。
RobP

2
视图是其自身的后代,因此不起作用...(虽然总体方法还可以,请参见其他答案)
Ixx

107

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

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

2
这比接受的答案对我来说更好。更简洁。
Suman Roy

1
@Ian在点击视图时不会调用此方法。
Praburaj

1
简明扼要的答案!
scurioni

公认的答案甚至没有用,但这对我来说是完美的。
Shalin Shah

@Prabu可能是因为手势识别器的代表必须在此之前设置
Kubba

23

对于Swift变体:

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

众所周知,isDescendantOfView返回一个Boolean值,该值指示接收方是给定视图的子视图还是与该视图相同的子视图。


1
不要忘记在您的课程中设置UIGestureRecognizerDelegate!
Fox5150 '16

4
除了执行if语句,您还不能返回它吗?return!touch.view.isDescendant(of:gestureRecognizer.view)此外,新语法迅速3 ^^
EdwardSanchez

12

在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


10

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

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
  }
}

4

清除Swift方式

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

至少在swipeGestureRecognizer中,永远不会在iOS 13中被调用。
Chewie The Chorkie

这和伊恩的答案有何不同?
sweetfa

3

请注意,gestureRecognizer API已更改为:

gestureRecognizer(_:应该接收:)

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

使用上面提供的许多示例,我没有收到此事件。以下是适用于当前版本的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
}

2

加上以上解决方案,请不要忘记检查User Interaction Enabled您的子视图。

在此处输入图片说明


2

我必须防止在子视图上显示手势。唯一有效的方法是允许并保留第一个视图,并在所有后续视图中阻止手势:

   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
     }

1

斯威夫特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
}

0

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

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
{
    return !(touch.view is UIControl)
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.