更新:问题似乎是对另一个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];
答案 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)
我写了以下解决方法。我将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];
}