如果UIGestureRecognizer触发,如何取消按钮点击?

时间:2011-05-31 14:21:31

标签: ios objective-c touch uigesturerecognizer

更新:问题似乎是对另一个GestureRecognizer的依赖失败。请参阅此问题下面的评论和测试项目!

在我的iPhone应用程序中,我有一个视图,其中有多个UIButtons作为子视图。该视图还有一个UITapGestureRecognizer,它可以用两根手指来监听。

当在视图上发生双指敲击时,我不希望按钮对敲击做出反应,即使其中一个手指在按钮内。我认为这是“cancelsTouchesInView”的用途,但这不起作用。

我现在的问题是:当手势被识别时,如何告诉我的按钮忽略点击?

编辑:这是我的手势识别器。

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];

4 个答案:

答案 0 :(得分:2)

根据Apple开发,这是一个错误。我向Apple提交了一份错误报告。 非常感谢您的提示,Deepak和gcamp!

错误报告:

  

要点:   将两个UITapGestureRecognizers添加到一个需要另一个失败的视图时(requiresGestureRecognizerToFail :),将忽略第一个手势识别器的cancelsTouchesInView属性。

     

重现步骤:   1.创建两个UITapGestureRecognizers(r1和r2)   2.将r1配置为需要两次触摸和一次点击并延迟touchesBegan   3.将r2配置为需要两次触摸和两次点击并延迟touchesBegan   4.配置r1以要求r2失败[r1 requiresGestureRecognizerToFail:r2]   5.将r1和r2添加到视图中   6.在视图中放置UIButton   7.在视图上用两根手指点击,按下按钮即可。

     

预期结果:   应识别r1并取消按钮(对于UITapGestureRecognizers,cancelsTouchesInView默认为YES)。

     

实际结果:   r1被识别,但按钮touchUpInside事件也被触发。

     

回归:   取消对r2的依赖后,cancelTouchesInView对r1工作正常(步骤4)。

答案 1 :(得分:1)

UIGestureRecognizer上有一个方法可以回答你的问题

- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer

基本上你要求两个水龙头识别器在接受单击之前失败。

所以,

[singleTapRecognizer requireGestureRecognizerToFail:twoTapRecognizer];

答案 2 :(得分:1)

使用delaysTouchesBegan属性。将其设置为YES

<强>替代

禁用按钮上的用户交互并附加如上所述的单指点按识别器。在点按处理程序中,检查点按是否在按钮的范围内。如果它在按钮的范围内,请执行[theButton sendActionsForControlEvents:UIControlEventTouchUpInside];。即使禁用了用户交互,这也会根据需要触发修饰事件。

答案 3 :(得分:0)

马克,我遇到了同样的错误。如果你发布你的雷达#,我会把它丢在bug报告器中。

我写了以下解决方法。我将UITapGestureRecognizer子类化,然后使用子类来跟踪触发手势动作的触摸。如果我们在touchesEnded中获得相同的触摸视图,我们将重定向到touchesCancelled。需要'immediateTarget',因为视图上的touchesEnded调用是在手势触摸后但在调用手势动作之前发生的。

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

在视图中:

@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's 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];
}