UITapGestureRecognizer touch self.view but ignores subviews

I need to implement a function that will call some code when double-clicking on self.view (view of UIViewCotroller ). But the problem is that I have another user interface object in this view, and I do not want to attach all recognizer objects to it. I found this method below, how to make a gesture in my opinion, and I know how it works. Right now I'm in front of a handicap that chooses to create this recognizer, ignoring the subview. Any ideas? Thank.

 UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)]; [doubleTap setNumberOfTapsRequired:2]; [self.view addGestureRecognizer:doubleTap]; 
+73
ios uitapgesturerecognizer
Apr 04 '13 at 14:53 on
source share
11 answers

You must accept the UIGestureRecognizerDelegate protocol inside the self object and call the method below to verify the view. Inside this method, check your opinion against touch.view and return the appropriate bool (Yes / No). Something like that:

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

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

+120
Apr 04 '13 at 2:59
source share

Another approach is to simply compare whether the touch view is a gesture view because the descendants will not pass the condition. Nice, simple single line:

 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { return touch.view == gestureRecognizer.view } 
+87
Jul 06 '16 at 6:00
source share

And for the Swift option:

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

It is useful to know isDescendantOfView returns a Boolean value indicating whether the receiver is a subordinate of this view or is identical to this view.

+19
Feb 28 '15 at 10:37
source share

Complete quick fix (delegate must be implemented and installed for recognizer (s)):

 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 } } } 
+7
Jul 28 '16 at 9:20
source share

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

Ask the delegate if the gesture recognizer should receive an object that represents the touch.

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



The full code below shows a possible implementation for gestureRecognizer(_:shouldReceive:) . With this code, clicking on the ViewController the ViewController (including imageView ) will not call the printHello(_:) method.

 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 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:) could be:

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

However, note that this alternative code does not check if touch.view gestureRecognizer.view .

+4
Jul 31 '18 at 20:54
source share

Option using CGPoint that you touch (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 } } 
+2
May 14 '18 at 14:33
source share

Clear Swift Way

 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { return touch.view == self.view } 
+2
Mar 31 '19 at 14:06
source share

Note that the gestureRecognizer API has changed to:

gestureRecognizer (_: shouldReceive :)

Note the underline indicator (skip) for the first label of the external parameter.

Using many of the examples above, I did not receive the event. Below is an example that works for current versions of 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 } 
+1
Sep 06 '17 at 15:45
source share

Plus the above solutions, don't forget to check User Interaction Enabled in your submission.

enter image description here

+1
Feb 25 '18 at 16:25
source share

Swift 4:

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

 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { if let touchedView = touch.view, touchedView.isDescendant(of: deductibleBackgroundView) { return false } return true } 
0
Nov 07 '18 at 18:37
source share

I had to prevent a gesture on the children's eyes. The only thing that worked was to allow and save the first view and prevent the gesture in all of the following views:

  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 } 
0
Mar 13 '19 at 15:17
source share



All Articles