How to block a gesture from superview to subview?

15,943

Solution 1

You have to implement the UIGestureRecognizerDelegate method:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;

And add your controller as the delegate of the gesture recognizers. Then, when two gesture recognizers respond to a gesture, this method will be called and here you can implement the logic you want for your app.

In the interface declaration of the controller you have to type:

@interface testcViewController () <UIGestureRecognizerDelegate>

Then, when creating the gesture recognizer:

UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipe)];
swipe.direction = UISwipeGestureRecognizerDirectionDown;
swipe.delegate = self;
[self.view addGestureRecognizer:swipe];

And then, finally, you add this method to the controller:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    BOOL shouldInteract = NO;
    //Here you decide whether or not the two recognizers whould interact.
    return shouldInteract;
}

EDIT You can also implement

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;

And here, detect if you have already presented the subviews, and block any gesture you want.

Solution 2

To block all gesture recognizers from superviews I created a UIGestureRecognizer sub class that will do just that when attached to a view. See the following code (taken from my WEPopover project):

#import "WEBlockingGestureRecognizer.h"
#import <UIKit/UIGestureRecognizerSubclass.h>

@implementation WEBlockingGestureRecognizer

- (id)init {
    return [self initWithTarget:self action:@selector(__dummyAction)];
}

- (id)initWithTarget:(id)target action:(SEL)action {
    if ((self = [super initWithTarget:target action:action])) {
        self.cancelsTouchesInView = NO;
    }
    return self;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    if (self.state == UIGestureRecognizerStatePossible) {
        self.state = UIGestureRecognizerStateBegan;
    }
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    self.state = UIGestureRecognizerStateRecognized;
}

- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer {
    return [self isGestureRecognizerAllowed:preventingGestureRecognizer];
}

- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer {
    return ![self isGestureRecognizerAllowed:preventedGestureRecognizer];
}

- (BOOL)shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return ![self isGestureRecognizerAllowed:otherGestureRecognizer];
}

- (BOOL)shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return NO;
}

- (BOOL)isGestureRecognizerAllowed:(UIGestureRecognizer *)gr {
    return [gr.view isDescendantOfView:self.view];
}

- (void)__dummyAction {

}

@end

If you want to block all gesture recognizers attached to parent views of some view, just do the following:

- (void)blockParentGesturesForView:(UIView *)v {
    [v addGestureRecognizer:[WEBlockingGestureRecognizer new]];
}

Solution 3

set userinteractionEnabled to NO of your subView

 subview.userinteractionEnabled=NO

if you dont want to disable userInteraction then use cancelsTouchesInView method

cancelsTouchesInView—If a gesture recognizer recognizes its gesture, it unbinds the remaining touches of that gesture from their view (so the window won’t deliver them). The window cancels the previously delivered touches with a (touchesCancelled:withEvent:) message. If a gesture recognizer doesn’t recognize its gesture, the view receives all touches in the multi-touch sequence.

Solution 4

Considering that I have a dialogView as a direct subview of my UIViewController's main view I'm attaching a gesture recognizer to the main view and do the following (setting my view controller as the gesture recognizer delegate):

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    let point = touch.location(in: view)
    return !dialogView.frame.contains(point)
} 
Share:
15,943
Q.u.a.n.g L.
Author by

Q.u.a.n.g L.

I make bugs.

Updated on June 04, 2022

Comments

  • Q.u.a.n.g L.
    Q.u.a.n.g L. almost 2 years

    I'm writing a module that everytime I swipe on a view, two sub views with a half size of the view will be added. Those subviews have their own gestures (eg: pan,...). The first time I swipe, it's OK because none of subview has been created. But once the subview been created, everytime I swipe, the swipe gesture is alway pass to its subviews. :(, so I have to swipe 2 times to divide.

    I want to know is there any way to block swipe passing to its subview? Thank you.

    UPDATE
    I used shouldRecognizeSimultaneouslyWithGestureRecognizer to make those gestures work simultaneously. But there's still have some problems. The parent view have its Swipe gesture, the subview have its Pan gesture. Since I use souldRecognizeSimultaneouslyWithGestureRecognizer, sometime when I'm panning, the swipe gesture triggers. So, you know how to disable Swipe while Pan is active in this situation?

  • Q.u.a.n.g L.
    Q.u.a.n.g L. almost 11 years
    So it'll also disable the subview gesture. I didn't want that. I want the subviews can be interacted.
  • The dude
    The dude almost 11 years
    Subviews also have gesture recognizers, so user interaction must be enabled.
  • lbsweek
    lbsweek over 5 years
    excellent ideas, this make gesture looks like event chain, subview will block superview gestures.