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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15814697/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 23:02:11  来源:igfitidea点击:

UITapGestureRecognizer tap on self.view but ignore subviews

iosuitapgesturerecognizer

提问by Matrosov Alexander

I need to implement a feature that will invoke some code when I double tap on the self.view (view of UIViewCotroller). But the problem that I have other UI object on this view and I don't want to attach any recognizer object to all of them. I found this method below how to make gesture on my view and I know how it works. Right now I am in front of handicap which way to choose for create this recognizer ignoring subview. Any ideas? Thanks.

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

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

回答by Ravi

You should adopt the UIGestureRecognizerDelegateprotocol inside the selfobject and call the below method for checking the view. Inside this method, check your view against touch.viewand return the appropriate bool (Yes/No). Something like this:

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

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

Edit: Please, also check @Ian's answer!

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

Swift 5

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

回答by Ian

Another approach is to just compare if the view of the touch is the gestures view, because descendants won't pass the condition. A nice, simple one-liner:

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

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

回答by Antoine

And for the Swift variant:

对于 Swift 变体:

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

Good to know, isDescendantOfViewreturns a Booleanvalue indicating whether the receiver is a subview of a given view or identical to that view.

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

回答by Imanou Petit

With Swift 5 and iOS 12, UIGestureRecognizerDelegatehas a method called gestureRecognizer(_:shouldReceive:). gestureRecognizer(_:shouldReceive:)has the following declaration:

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

Ask the delegate if a gesture recognizer should receive an object representing a touch.

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

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


The complete code below shows a possible implementation for gestureRecognizer(_:shouldReceive:). With this code, a tap on a subview of the ViewController's view (including imageView) won't trigger the printHello(_:)method.

下面的完整代码显示了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")
    }

}


An alternative implementation of gestureRecognizer(_:shouldReceive:)can be:

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

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

Note however that this alternative code does not check if touch.viewis a subview of gestureRecognizer.view.

但是请注意,此替代代码不会检查是否touch.viewgestureRecognizer.view.

回答by Arru

Complete swift solution (delegate must be implemented AND set for recognizer(s) ):

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

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

回答by CoachThys

Variant using CGPoint you touch (SWIFT 4.0)

使用您触摸的 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
  }
}

回答by Musa almatri

Clear Swift way

清除 Swift 方式

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

回答by Rico

Note that the gestureRecognizer APIhas changed to:

请注意,gestureRecognizer API已更改为:

gestureRecognizer(_:shouldReceive:)

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

Take particular note of the underscore (skip) indicator for the first parameter's external label.

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

Using many of the examples provided above, I was not receiving the event. Below is a an example that works for current versions of Swift (3+).

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

回答by MrDEV

Plus the above solutions, do not forget to check User Interaction Enabledof your sub-view.

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

enter image description here

在此处输入图片说明

回答by Jonathan Cabrera

Swift 4:

斯威夫特 4:

touch.viewis now an optional, so based on @Antoine's answer:

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

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