By Mustafa


2011-01-28 05:37:02 8 Comments

I have a view hierarchy that looks something like this:

UIView (A)
UIView > UIImageView
UIView > UIView (B)
UIView > UIView (B) > Rounded Rect Button
UIView > UIView (B) > UIImageView
UIView > UIView (B) > UILabel

I've attached gesture recognizer(s) to my UIView (B). The problem that i'm facing is that i don't get any actions for the Rounded Rect Button which is inside the UIView (B). The singleTap gesture recognizer captures/overrides the button's Touch Up Inside event.

How can i make it work? I thought that the responder chain hierarchy will make sure that the button touch event will be given preference, and it WILL get triggered! What am i missing?

Here's some related code:

#pragma mark -
#pragma mark View lifecycle (Gesture recognizer setup)

- (void)viewDidLoad {
    [super viewDidLoad];

    // double tap gesture recognizer
    UITapGestureRecognizer *dtapGestureRecognize = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTapGestureRecognizer:)];
    dtapGestureRecognize.delegate = self;
    dtapGestureRecognize.numberOfTapsRequired = 2;
    [self.viewB addGestureRecognizer:dtapGestureRecognize];

    // single tap gesture recognizer
    UITapGestureRecognizer *tapGestureRecognize = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTapGestureRecognizer:)];
    tapGestureRecognize.delegate = self;
    tapGestureRecognize.numberOfTapsRequired = 1;
    [tapGestureRecognize requireGestureRecognizerToFail:dtapGestureRecognize];
    [self.viewB addGestureRecognizer:tapGestureRecognize];

    // add gesture recodgnizer to the grid view to start the edit mode
    UILongPressGestureRecognizer *pahGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressGestureRecognizerStateChanged:)];
    pahGestureRecognizer.delegate = self;
    pahGestureRecognizer.minimumPressDuration = 0.5;
    [self.viewB addGestureRecognizer:pahGestureRecognizer];

    [dtapGestureRecognize release];
    [tapGestureRecognize release];
    [pahGestureRecognizer release];
}

#pragma mark -
#pragma mark Button actions

- (IBAction)buttonTouchUpInside:(id)sender {
    NSLog(@"%s, %@", __FUNCTION__, sender);
}

#pragma mark -
#pragma mark Gesture recognizer actions


- (void)singleTapGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer {
    NSLog(@"%s", __FUNCTION__);
}

- (void)doubleTapGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer {
    NSLog(@"%s", __FUNCTION__);
}

- (void)longPressGestureRecognizerStateChanged:(UIGestureRecognizer *)gestureRecognizer {

    switch (gestureRecognizer.state) {

        case UIGestureRecognizerStateEnded: {
            NSLog(@"%s", __FUNCTION__);

            break;
        }
        default:
            break;
    }
}

6 comments

@Marcelo Gracietti 2016-12-07 16:12:37

Here is a Swift 3.0 version:

extension UIViewController: UIGestureRecognizerDelegate {

public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if touch.view is UIButton {
        return false
    }
    return true
}

Don't forget to:

Make your tapper object delegate to self (e.g: tapper.delegate = self)

@rsc 2015-12-28 19:28:14

Here is a Swift version:

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

Don't forget to:

  1. Make you class conform to UIGestureRecognizerDelegate
  2. Make your tapper object delegate to self (e.g: tapper.delegate = self)

@shannoga 2011-01-28 05:49:34

In the "shouldReceiveTouch" method you should add a condition that will return NO if the touch is in the button.

This is from apple SimpleGestureRecognizers example.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {

    // Disallow recognition of tap gestures in the segmented control.
    if ((touch.view == yourButton)) {//change it to your condition
        return NO;
    }
    return YES;
}

hope it will help

Edit

As Daniel noted you must conform to UIGestureRecognizerDelegate for it to work.

shani

@Mustafa 2011-01-28 07:29:25

Ah!!! Yes, i forgot about this -gestureRecognizer:shouldReceiveTouch: method. Thanks for pointing me in the right direction. Although this solves my problem, but i still don't know why the responder hierarchy is not working in this case. It probably should, but it's not being handled by Apple.

@Sylvain G. 2011-02-17 12:48:15

UIGestureRecognizer behavior is clearly described as separate from the "normal" responder chain. This is a design decision by Apple.

@Daniel 2012-02-27 14:46:31

Remember to conform to the UIGestureRecognizerDelegate Protocol and set the UIGestureRecognizer's delegate to that object.

@clozach 2012-09-05 06:58:04

For broader use, consider some variant on Ramesh's or flypig's test clauses. In my case, for example, I ended up with the line: if ( [touch.view isKindOfClass:[UIControl class]] || [[touch.view superview] isKindOfClass:[UITableViewCell class]] ) {... Note that tableview cells get "touched" in their contentView, which is a member of a private class, so there we check the superview's class instead.

@Luis Artola 2013-03-03 07:17:38

Thank you for the answer! In my case, I ran into the same problem but I discovered it only until I tested in the actual device. Running the app in the simulator without using UIGestureDelegate as indicated in this answer worked as expected though incorrectly.

@Warewolf 2013-06-20 16:03:20

can we detect direction within this method

@Lonkly 2014-11-10 15:56:27

The best solution is to my mind using code snippet below:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    CGPoint touchLocation = [touch locationInView:self.view];
    return !CGRectContainsPoint(self.menuButton.frame, touchLocation);
}

@flypig 2012-09-04 07:53:08

Generally speaking, we use below delegate method to avoid the touch in all kinds of UIControls:

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

Note: DO NOT do this check (check the recognizer.view class type) the gestureRecognizerShouldBegin, it won't work.

@Ramesh Chandran A 2011-07-26 05:53:06

I also had the same problem , then i tried with

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{

    if ([touch.view isKindOfClass:[UIButton class]]) {      //change it to your condition
        return NO;
    }
    return YES;
}

It is working now perfectly.........

@zero3nna 2013-01-28 14:15:25

i only missed the myGestureRecognizer.delegate = self;

Related Questions

Sponsored Content

1 Answered Questions

[SOLVED] UINaviagtionBar not displaying?

  • 2010-05-24 09:03:54
  • Tirth
  • 101 View
  • 0 Score
  • 1 Answer
  • Tags:   iphone

2 Answered Questions

[SOLVED] UIGestureRecognizer and UITextView

0 Answered Questions

UIScrollView Not Working Properly For iPhone6 and iPhone 6Plus

3 Answered Questions

1 Answered Questions

[SOLVED] Two UIGestureRecognizers and UIButton

1 Answered Questions

[SOLVED] Create custom UIGestureRecognizer

1 Answered Questions

[SOLVED] drawRect and setNeedsDisplay never be called

2 Answered Questions

[SOLVED] UIButton in a custom UIView trouble

Sponsored Content