【问题标题】:How to cancel button tap if UIGestureRecognizer fires?如果 UIGestureRecognizer 触发,如何取消按钮点击?
【发布时间】:2011-09-05 13:11:33
【问题描述】:

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

在我的 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];

【问题讨论】:

  • 在被 Deepak 和 gcamp 确认取消TouchesInView 应该可以工作之后,我进一步调查并发现了这个问题的根源:在我的示例中的手势旁边,我还有一个两指、两点触摸手势识别器.上面的 doubleTap 识别器配置为要求此 twoFingerDoubleTap 失败。出于某种原因,这会阻止“cancelsTouchesInView”工作。有谁知道为什么会这样?
  • 我为此做了一个测试项目:dl.dropbox.com/u/3996208/iPhone/toucheventtest.zip

标签: ios objective-c touch uigesturerecognizer


【解决方案1】:

根据 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 步)。

【讨论】:

    【解决方案2】:

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

    - (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer
    

    基本上你要求两次点击识别器在接受一次点击之前失败。

    所以,

    [singleTapRecognizer requireGestureRecognizerToFail:twoTapRecognizer];
    

    【讨论】:

    • 所以我必须为每个按钮添加一个点击手势识别器(我有很多)?这会覆盖正常的触摸事件吗?
    • 这不会覆盖正常的触摸事件,但如果您删除您已经制作的addTarget:action:forControlEvents: 并仅将目标/动作放在单击识别器中,那应该可以工作。
    【解决方案3】:

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

    替代方案

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

    【讨论】:

    • 我已经在这样做了,但这并没有帮助。应该在原帖中提到,对不起。
    • 当你使用这个时会有什么行为?
    • 手势被识别,但按钮仍然被按下
    • 删除 delaysTouchesEndedcancelsTouchesInView 并尝试。我制作了一个示例应用程序,它正在运行。
    • 删除了两者(尽管默认值应该相同?)但没有帮助。手势识别器仍然触发并且 touchUpInside 动作(与 Interfacebuilder 连接)被调用......必须在那里做一些非常奇怪的事情...... :-/ 我添加了一些日志记录,首先是手势识别器触发,然后是按钮。好像cancelsTouchesInView(默认为NO)被忽略了?
    【解决方案4】:

    马克,我遇到了同样的错误。如果你发布你的雷达#,我会在 bugreporter 中欺骗它。

    我编写了以下解决方法。我将 UITapGestureRecognizer 子类化,然后使用子类来跟踪触发手势动作的触摸。如果我们在视图的 touchesEnded 中获得相同的触摸,我们将重定向到 touchesCancelled。 'immediateTarget' 是必需的,因为视图上的 touchesEnded 调用发生在手势的 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];
    }
    

    【讨论】:

    • 嘿埃文,谢谢你的回答。我提交的错误的雷达 ID 是 9540729
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-14
    • 2012-08-06
    • 2015-11-27
    相关资源
    最近更新 更多