How to cancel a button click when starting UIGestureRecognizer?

Update. The problem seems to be related to another GestureRecognizer error. See comments and test project below this question!

In my iPhone application, I have a view with several UIButtons as subviews. The view also has a UITapGestureRecognizer that listens for taps with two fingers.

When two fingers appear in the view, I don’t want the buttons to respond to the tap, even if one of the fingers was inside the button. I thought this is what “cancels ToursInView” for, but it does not work.

Now my question is: how do I tell my buttons to ignore taps when a gesture is recognized?

Edit: This is my gesture recognizer.

UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTapped:)];
[doubleTap setNumberOfTouchesRequired:2];
[doubleTap setNumberOfTapsRequired:1];
[doubleTap setCancelsTouchesInView:YES];
[doubleTap setDelaysTouchesBegan:YES];
[doubleTap setDelaysTouchesEnded:YES];
[self.view addGestureRecognizer:doubleTap];
[doubleTap release];
+3
source share
4

Apple dev, . Apple. , Deepak gcamp!

:

: UITapGestureRecognizers , , (requireGestureRecognizerToFail:), cancelsTouchesInView .

: 1. UITapGestureRecognizers (r1 r2) 2. r1, Began 3. r2, Began 4. r1, , r2 [r1 GestureRecognizerToFail: r2] 5. r1 r2 6. UIButton 7. , .

: r1 , ( TouchesInView YES UITapGestureRecognizers).

: r1 , UpInside.

: cancelTouchesInView r1 r2 ( 4).

+2

UIGestureRecognizer ,

- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer

, , .

,

[singleTapRecognizer requireGestureRecognizerToFail:twoTapRecognizer];
+1

delaysTouchesBegan. YES.

Alternative

, . , . , [theButton sendActionsForControlEvents:UIControlEventTouchUpInside];. , .

+1
, . #, bugreporter.

I wrote the following workaround. I am a subclass of UITapGestureRecognizer, then use the subclass to track the touches that triggered the gestures. If we get the same touches in the contacts that you see on the screen, we redirect to touches. "instantTarget" is necessary because a call to touchEnded in the view occurs after the gesture touches Ended, but before the gesture is called.

RPTapGestureRecognizer.h:

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

@interface RPTapGestureRecognizer : UITapGestureRecognizer

@property (nonatomic, retain) NSSet *lastTouches;

- (void)setImmediateTarget:(id)inTarget action:(SEL)inAction;

@end

RPTapGestureRecognizer.m:

#import "RPTapGestureRecognizer.h"

@interface RPTapGestureRecognizer ()
@property (nonatomic) SEL immediateAction;
@property (nonatomic, assign) id immediateTarget;
@end

@implementation RPTapGestureRecognizer

@synthesize lastTouches;
@synthesize immediateAction, immediateTarget;

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    self.lastTouches = nil;

    [super touchesBegan:touches withEvent:event];
}

- (void)setImmediateTarget:(id)inTarget action:(SEL)inAction
{
    self.immediateTarget = inTarget;
    self.immediateAction = inAction;
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    UIGestureRecognizerState startingState, finalState;

    startingState = self.state;
    [super touchesEnded:touches withEvent:event];
    finalState = self.state;

    if (startingState != finalState &&
        finalState == UIGestureRecognizerStateEnded) {
        /* Must copy; the underlying NSCFSet will be modified by the superclass, removing the touches */
        self.lastTouches = [[touches copy] autorelease];

        if (self.immediateAction)
            [self.immediateTarget performSelector:self.immediateAction
                                       withObject:self];
    }
}

- (void)dealloc
{
    self.lastTouches = nil;
    self.immediateTarget = nil;

    [super dealloc];
}

@end

In view:

@synthesize lastTapGesture

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (self.lastTapGesture && [self.lastTapGesture.lastTouches isEqualToSet:touches]) {
        [self touchesCancelled:touches withEvent:event];
        self.lastTapGesture = nil;
        return;
    }
 /* touches ended implementation here */
}

 - (void)willHandleMouseTapGesture:(RPTapGestureRecognizer *)tapGesture
{
    /* Because one tap gesture is dependent upon another, the touchesEnded method is still going to be called, instead of touchesCanceled.
     * This is an Apple bug against all versions of iOS at least up to 5.0. It will be called in the run loop before tapGesture action is called.
     *
     * See http://stackoverflow.com/questions/6188997/how-to-cancel-button-tap-if-uigesturerecognizer-fires/6211922#6211922 for details.
     */
    self.lastTapGesture = tapGesture;
}

- (void)configureGestures
{
    /* Two finger taps */
     RPTapGestureRecognizer *tapGestureOne;
     tapGestureOne = [[[RPTapGestureRecognizer alloc] initWithTarget:self 
                                                              action:@selector(handleMouseTapGesture:)] autorelease];
     tapGestureOne.numberOfTapsRequired = 1;
     tapGestureOne.numberOfTouchesRequired = 2;
     [tapGestureOne setImmediateTarget:self action:@selector(willHandleMouseTapGesture:)];
     [self addGestureRecognizer:tapGestureOne];
     [myGestures addObject:tapGestureOne];

     RPTapGestureRecognizer *tapGestureTwo;
     tapGestureTwo = [[[RPTapGestureRecognizer alloc] initWithTarget:self 
                                                              action:@selector(handleMouseTapGesture:)] autorelease];
     tapGestureTwo.numberOfTapsRequired = 2;
     tapGestureTwo.numberOfTouchesRequired = 2;
     [tapGestureTwo setImmediateTarget:self action:@selector(willHandleMouseTapGesture:)];
     [self addGestureRecognizer:tapGestureTwo];
}
0
source

All Articles