105

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

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

12 回答 12

170

您应该采用对象UIGestureRecognizerDelegate内部的协议self并调用以下方法来检查视图。在此方法中,检查您的视图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
}
于 2013-04-04T14:59:28.863 回答
122

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

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view == gestureRecognizer.view
}
于 2016-07-06T06:00:58.357 回答
23

对于 Swift 变体:

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

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

于 2015-02-28T10:37:43.000 回答
18

在 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.viewgestureRecognizer.view.

于 2018-07-31T20:54:52.593 回答
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
        }
    }
}
于 2016-07-28T09:20:51.197 回答
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
  }
}
于 2018-05-14T14:33:30.650 回答
6

清除 Swift 方式

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view == self.view
}
于 2019-03-31T14:06:17.733 回答
3

请注意,gestureRecognizer API已更改为:

手势识别器(_:应该接收:)

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

使用上面提供的许多示例,我没有收到该事件。下面是一个适用于当前版本的 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
}
于 2017-09-06T15:45:23.637 回答
2

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

在此处输入图像描述

于 2018-02-25T16:25:35.940 回答
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
     }
于 2019-03-13T15:17:53.643 回答
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
}
于 2018-11-07T18:37:22.083 回答
0

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

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
{
    return !(touch.view is UIControl)
}
于 2020-07-01T13:37:01.507 回答