UIGestureRecognizer blocks subview to handle touch events

I am trying to understand how this is done correctly. I tried to portray the situation: enter image description here

I am adding a UITableView as a subitem of a UIView . UIView responds to tap- and pinchGestureRecognizer , but at the same time tableview stops responding to these two gestures (it still responds to swipes).

I did this to work with the following code, but this is obviously not a good solution, and I'm sure there is a better way. This is placed in a UIView (supervisor):

 -(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { if([super hitTest:point withEvent:event] == self) { for (id gesture in self.gestureRecognizers) { [gesture setEnabled:YES]; } return self; } for (id gesture in self.gestureRecognizers) { [gesture setEnabled:NO]; } return [self.subviews lastObject]; } 
+72
ios objective-c uiview uigesturerecognizer uiresponder
Mar 07 '11 at 17:32
source share
10 answers

I had a very similar problem and found my solution in this SO question . In general, designate yourself as a delegate for your UIGestureRecognizer , and then check the target view before allowing the recognizer to process the melody. Corresponding delegate method:

 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch 
+174
May 04 '11 at 17:35
source share

Blocking touch events on subzones is the default behavior. You can change this behavior:

 UITapGestureRecognizer *r = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(agentPickerTapped:)]; r.cancelsTouchesInView = NO; [agentPicker addGestureRecognizer:r]; 
+90
Jul 20 '12 at 6:05
source share

One possibility is to subclass your gesture recognizer (if you haven’t already) and override -touchesBegan:withEvent: so that it determines whether each touch starts in the excluded subzone and calls -ignoreTouch:forEvent: for that touch, if it is .

Obviously, you also need to add a property to keep track of the excluded subview, or perhaps better, an array of excluded subviews.

+4
Mar 08 2018-11-11T00:
source share

I was showing a dropdown that had its own tableview. As a result, touch.view sometimes returned classes of type UITableViewCell . I had to go through the superclass (s) to make sure that it was a subclass that I thought was:

 -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { UIView *view = touch.view; while (view.class != UIView.class) { // Check if superclass is of type dropdown if (view.class == dropDown.class) { // dropDown is an ivar; replace with your own NSLog(@"Is of type dropdown; returning NO"); return NO; } else { view = view.superview; } } return YES; } 
+4
Jul 24 '13 at 11:43
source share

You can do without inheriting any class.

you can check gestureRecognizers in the gesture callback selector

if view.gestureRecognizers does not contain your gestureRecognizer, just ignore it

eg

 - (void)viewDidLoad { UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)]; singleTapGesture.numberOfTapsRequired = 1; } 

check view.gestureRecognizers here

 - (void)handleSingleTap:(UIGestureRecognizer *)gestureRecognizer { UIEvent *event = [[UIEvent alloc] init]; CGPoint location = [gestureRecognizer locationInView:self.view]; //check actually view you hit via hitTest UIView *view = [self.view hitTest:location withEvent:event]; if ([view.gestureRecognizers containsObject:gestureRecognizer]) { //your UIView //do something } else { //your UITableView or some thing else... //ignore } } 
+2
Apr 14 '14 at 5:09
source share

I created a subclass of UIGestureRecognizer, designed to block all gesture recognizers attached to supervisors of a certain kind.

This is part of my WEPopover project. You can find it here .

+1
Sep 18 '14 at 2:56
source share

Based on @Pin Shih Wang answer . We ignore all taps other than those displayed in the view containing the gesture recognizer. All taps are redirected to the view hierarchy as usual, as we set tapGestureRecognizer.cancelsTouchesInView = false . Here is the code in Swift3 / 4:

 func ensureBackgroundTapDismissesKeyboard() { let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap)) tapGestureRecognizer.cancelsTouchesInView = false self.view.addGestureRecognizer(tapGestureRecognizer) } @objc func handleTap(recognizer: UIGestureRecognizer) { let location = recognizer.location(in: self.view) let hitTestView = self.view.hitTest(location, with: UIEvent()) if hitTestView?.gestureRecognizers?.contains(recognizer) == .some(true) { // I dismiss the keyboard on a tap on the scroll view // REPLACE with own logic self.view.endEditing(true) } } 
+1
Sep 27 '17 at 12:33 on
source share

You can turn it off and on .... in my code I did something like this, since I needed to turn it off when the keyboard wasn’t showing, you can apply it to your situation:

calling this is viewdidload etc:

 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; [center addObserver:self selector:@selector(notifyShowKeyboard:) name:UIKeyboardDidShowNotification object:nil]; [center addObserver:self selector:@selector(notifyHideKeyboard:) name:UIKeyboardWillHideNotification object:nil]; 

then create two methods:

 -(void) notifyShowKeyboard:(NSNotification *)inNotification { tap.enabled=true; // turn the gesture on } -(void) notifyHideKeyboard:(NSNotification *)inNotification { tap.enabled=false; //turn the gesture off so it wont consume the touch event } 

What this does is turn off the taps. I had to include tap in the instance variable and release it in dealloc, though.

0
Dec 01 '12 at 4:12
source share

I did popover too, and this is how I did it

 func didTap(sender: UITapGestureRecognizer) { let tapLocation = sender.locationInView(tableView) if let _ = tableView.indexPathForRowAtPoint(tapLocation) { sender.cancelsTouchesInView = false } else { delegate?.menuDimissed() } } 
0
Oct 29 '15 at 20:06
source share

implement a delegate for all parentView resolvers and put the gesureRecognizer method in the delegate, which is responsible for running the resolvers simultaneously:

 func gestureRecognizer(UIGestureRecognizer, shouldBeRequiredToFailByGestureRecognizer:UIGestureRecognizer) -> Bool { if (otherGestureRecognizer.view.isDescendantOfView(gestureRecognizer.view)) { return true } else { return false } 

}

U can use crash methods if you want children to start, but not parent recognizers:

https://developer.apple.com/reference/uikit/uigesturerecognizerdelegate

0
Dec 12 '16 at 11:01
source share



All Articles