8

I have a bunch of UIButtons within a UIView within a UIScrollView. I'm trying to add a tap recognizer to the scroll view. The tap recognizer fires, but now none of my buttons work.

I know that in iOS5, UIScrollView can somehow pass a touch event down to the control hierarchy after being done with it. Anyone can help me figure out how to do this?

Lorenzo B
  • 33,216
  • 24
  • 116
  • 190
Alex Stone
  • 46,408
  • 55
  • 231
  • 407

3 Answers3

15

Set the UIGestureRecognizer property cancelsTouchesInView to NO.

UITapGestureRecognizer *singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self 
                                                                                             action:@selector(singleTap:)];
singleTapGestureRecognizer.numberOfTapsRequired = 1;
singleTapGestureRecognizer.enabled = YES;
singleTapGestureRecognizer.cancelsTouchesInView = NO;
[tapableView addGestureRecognizer:singleTapGestureRecognizer];
[singleTapGestureRecognizer release];

From UIGestureRecognizer Class Reference

A Boolean value affecting whether touches are delivered to a view when a gesture is recognized.

When this property is YES (the default) and the receiver recognizes its gesture, the touches of that gesture that are pending are not delivered to the view and previously delivered touches are cancelled through a touchesCancelled:withEvent: message sent to the view. If a gesture recognizer doesn’t recognize its gesture or if the value of this property is NO, the view receives all touches in the multi-touch sequence.

Till
  • 27,559
  • 13
  • 88
  • 122
  • Ok, I didn't see that was wanted behavior, but if it is for your case and @AlexStone 's case then jolly good! :-) – hfossli Feb 07 '13 at 12:04
2

For me a combo of the above answers worked

UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(userDidTap:)];
tapRecognizer.cancelsTouchesInView = YES;
tapRecognizer.delegate = self;
[tapRecognizer requireGestureRecognizersToFail:self.scrollView.gestureRecognizers];
[self.view addGestureRecognizer:tapRecognizer];

-

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([touch.view.superview isKindOfClass:[UIButton class]] || [touch.view isKindOfClass:[UIButton class]])
    {
        return NO;
    }
    return YES;
}
hfossli
  • 22,616
  • 10
  • 116
  • 130
  • This would make the gesture recognizer fail on the button - not sure how this correlates with the question. – Till Feb 07 '13 at 11:25
  • Yes, it will. In my case that is wanted behavior. The question is not clear on this point to my understanding. – hfossli Feb 07 '13 at 12:05
0

You could also use the gestureRecognizer:shouldReceiveTouch: method of the UIGestureRecognizerDelegate, which is documented here, to accomplish the same thing. It offers a little more flexibility, for instance, if you want to cancel certain touches, but not others. Here's an example,

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

    return YES;
}
john
  • 3,043
  • 5
  • 27
  • 48
  • That actually is not the same thing. When returning NO, the gesture recognizer does not handle the touch. When returning YES, the UIView does not handle the touch. cancelsTouchesInView set to NO means both, the gesture recognizer AND the view receive the touch. – Till Nov 24 '11 at 00:32
  • You are correct. If you wanted to accomplish both you could potentially handle the touch event here *before* returning NO. – john Nov 24 '11 at 00:41