UITapGestureRecognizer - 单击和双击

170
我试图为一个视图添加两个UITapGestureRecognizers,一个用于单击事件,另一个用于双击事件。单击识别器可以正常工作(独立使用时),但是我好像无法使双击识别器正常工作。已经尝试使用cancelsTouchesInView、delaysTouchesBegan和delaysTouchesEnded等属性进行实验,但仍然不能正常工作。
当我双击时,单击识别器总是会被激活,并且双击事件也会被发送到超级视图。但是自定义的双击识别器似乎根本没有被通知过。
文档似乎建议可以使用上述三个属性来实现此目的,但我不确定应该在哪个识别器(单击、双击或两者)以及设置哪些值。希望熟悉此问题的人能提供帮助。
以下是最新更新的代码块:
// ****** gesture recognizers ******

- (void)addSingleAndDoubleTapGestureRecognizersToView:(UIView *)view
{
    // single tap    
    UITapGestureRecognizer *singleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget: tableViewController action: @selector(handleSingleTapOnView:)];                                 
    [singleTapRecognizer setNumberOfTouchesRequired:1];
    [view addGestureRecognizer: singleTapRecognizer];

    // double tap 
    UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget: tableViewController action: @selector (handleDoubleTapOnView:)];        
    [doubleTapRecognizer setNumberOfTouchesRequired:2];         
    [singleTapRecognizer requireGestureRecognizerToFail: doubleTapRecognizer];
    [view addGestureRecognizer: doubleTapRecognizer];         
}

- (void)handleSingleTapOnView:(id)sender
{

}

- (void)handleDoubleTapOnView:(id)sender
{

}

32
随机评论:在变量名中使用下划线会让大多数Objective-C程序员感到不适。更糟的是,你的变量名不具有描述性。为什么不直接使用 singleTapRecognizer 和 doubleTapRecognizer(或类似的名称)?这需要多打几个字,但可以使你的代码可读性无限提高。 - UIAdam
感谢您的建议,我同意变量名应该更加具有描述性。但是这段代码还没有最终确定,我会按照建议发布更加完善和具有描述性的代码... - Stanley
如果其他人有类似的问题。我在Swift中很难同时使用双击和单击识别器(单击正常工作,但没有双击)。我使用Storyboard将识别器拖到场景中。我在viewDidLoad中添加了处理程序和语句: singleTapRecognizer.requireGestureRecognizerToFail(doubleTabRecognizer) 但仍然没有双击。我缺少的是将双击识别器图标(从场景的顶部行)拖动到要识别双击的视图部分。 - Paulus
1
@UIAdam在Objective-C中使用下划线作为私有变量名的前缀是相当普遍的,不是吗?(我来自Swift背景) - Charlton Provatas
9个回答

457
UITapGestureRecognizer *singleTap = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doSingleTap)] autorelease];
singleTap.numberOfTapsRequired = 1; 
[self.view addGestureRecognizer:singleTap];

UITapGestureRecognizer *doubleTap = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doDoubleTap)] autorelease];
doubleTap.numberOfTapsRequired = 2; 
[self.view addGestureRecognizer:doubleTap];

[singleTap requireGestureRecognizerToFail:doubleTap];

注意:如果你正在使用numberOfTouchesRequired,它必须是.numberOfTouchesRequired = 1;

对于Swift:

let singleTapGesture = UITapGestureRecognizer(target: self, action: #selector(didPressPartButton))
singleTapGesture.numberOfTapsRequired = 1
view.addGestureRecognizer(singleTapGesture)

let doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(didDoubleTap))
doubleTapGesture.numberOfTapsRequired = 2
view.addGestureRecognizer(doubleTapGesture)

singleTapGesture.require(toFail: doubleTapGesture)

谢谢你的回答,我已经尝试了你的代码,但它仍然无法正常工作。我肯定还是漏掉了什么。你有更多的提示吗? - Stanley
@Stanley:Anil的答案应该可以,当然你还需要提供处理程序(doSingleTap、doDoubleTap)。 - Rok Jarc
@Anil 提供的解决方案确实有效。在我的情况下,一些超级视图可能会阻止双击,同时允许单击通过... - Stanley
10
这个解决方案对我有效。单击选择器会有一定的延迟后触发。 - Vladimir Obrizan
如果您尝试在同一个处理程序中处理事件,则可能无法正常工作。我曾尝试使用numberOfTouches来区分它们,但并没有起作用。 - Cristi Băluță
显示剩余2条评论

53

Swift 3 解决方案:

let singleTap = UITapGestureRecognizer(target: self, action:#selector(self.singleTapAction(_:)))
singleTap.numberOfTapsRequired = 1
view.addGestureRecognizer(singleTap)

let doubleTap = UITapGestureRecognizer(target: self, action:#selector(self.doubleTapAction(_:)))
doubleTap.numberOfTapsRequired = 2
view.addGestureRecognizer(doubleTap)

singleTap.require(toFail: doubleTap)

在代码行singleTap.require(toFail: doubleTap)中,我们强制单次点击等待,确保该点击事件不是双击。


33
您需要使用requireGestureRecognizerToFail:方法。类似这样: [singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];

刚刚又尝试使用“Fail”方法调用两个识别器,但仍然不起作用。如果您之前已经成功实现了双击功能,请与我分享更多的经验。 - Stanley
1
我已经尝试了这段完全相同的代码(与Anil发布的更详细的代码相同),它对我有效。 - UIAdam
你的代码仍然会干扰delaysTouches等内容。让它完全按照Anil的代码来实现。只在我们现有的方式上使用一次requireGestureRecognizerToFail。 - UIAdam
我会按照你的建议在今天晚些时候或明天完成,因为我现在时间有点紧。感谢你的帮助。 - Stanley
关于结果,@Adam W,令人沮丧的是双击仍然没有被注册。有趣的是,如果我将numberOfTouchesRequired从2更改为1,双击识别器就会被激活。我认为我的问题可能与响应者链有关。识别器所附加的UIView是通过代码创建的(而不是使用IB),我没有明确将其设置为第一个响应者。因此,在第一次轻拍后,第一响应者状态是否可以从UIView转移到其他地方? - Stanley
显示剩余2条评论

15

//----首先,您需要分配双击和单击手势-------//

UITapGestureRecognizer* doubleTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : @selector (handleDoubleTap:)];

UITapGestureRecognizer* singleTap = [[UITapGestureRecognizer alloc] initWithTarget : self action : @selector (handleSingleTap:)];

[singleTap requireGestureRecognizerToFail : doubleTap];
[doubleTap setDelaysTouchesBegan : YES];
[singleTap setDelaysTouchesBegan : YES];

//-----------------------点击次数----------------//

[doubleTap setNumberOfTapsRequired : 2];
[singleTap setNumberOfTapsRequired : 1];

//------- 在视图或按钮上添加双击和单击手势--------//

[self.view addGestureRecognizer : doubleTap];
[self.view addGestureRecognizer : singleTap];

10

不确定这是否完全符合你的要求,但我没有使用手势识别器来实现单击/双击。在UITableView中使用时,我将该代码放在didSelectRowAtIndexPath方法中。

    tapCount++;
    switch (tapCount)
    {
        case 1: //single tap
            [self performSelector:@selector(singleTap:) withObject: indexPath afterDelay: 0.2];
            break;
        case 2: //double tap
            [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(singleTap:) object:indexPath];
            [self performSelector:@selector(doubleTap:) withObject: indexPath];
            break;
        default:
            break;
    }
    if (tapCount>2) tapCount=0;

方法singleTap和doubleTap只是带有NSIndexPath参数的void方法:

- (void)singleTap:(NSIndexPath *)indexPath {
  //do your stuff for a single tap
}

- (void)doubleTap:(NSIndexPath *)indexPath {
  //do your stuff for a double tap
}

希望能有所帮助


谢谢回答,@Novarg。你是否遇到了我所遇到的关于doubleTap识别器的问题? - Stanley
@Stanley 不是的,singleTap 在触发前等待0.2秒。如果在此期间第二次点击被激活,则 singleTap 方法会被取消。 - Novarg
看起来非常有前途,感谢您的帮助。今天时间又不够了。明天一定会尝试一下 :) - Stanley
创意答案 ;) - Michael
非常好的解决方案。我只需要在每个处理程序(singleTap,doubleTap)中添加tapCount=0,以确保后续触摸也能被识别。 - Sirio

3

Swift 2的解决方案:

let singleTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleSingleTap))
singleTapGesture.numberOfTapsRequired = 1 // Optional for single tap
view.addGestureRecognizer(singleTapGesture)

let doubleTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap))
doubleTapGesture.numberOfTapsRequired = 2
view.addGestureRecognizer(doubleTapGesture)

singleTapGesture.requireGestureRecognizerToFail(doubleTapGesture)

1
我实现了UIGestureRecognizerDelegate方法来检测单击和双击手势。
只需要这样做。
 UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleDoubleTapGesture:)];
    [doubleTap setDelegate:self];
    doubleTap.numberOfTapsRequired = 2;
    [self.headerView addGestureRecognizer:doubleTap];

    UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleSingleTapGesture:)];
    singleTap.numberOfTapsRequired = 1;
    [singleTap setDelegate:self];
    [doubleTap setDelaysTouchesBegan:YES];
    [singleTap setDelaysTouchesBegan:YES];
    [singleTap requireGestureRecognizerToFail:doubleTap];
    [self.headerView addGestureRecognizer:singleTap];

然后实现这些委托方法。
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
    return  YES;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    return YES;
}

0
一些视图已内置了双击识别器(例如MKMapView)。为了解决这个问题,您需要实现UIGestureRecognizerDelegate方法shouldRecognizeSimultaneouslyWithGestureRecognizer并返回YES
首先,实现您的双击和单击识别器:
// setup gesture recognizers
UITapGestureRecognizer* singleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                                                action:@selector(mapViewTapped:)];
singleTapRecognizer.delegate = self;
singleTapRecognizer.numberOfTapsRequired = 1;


UITapGestureRecognizer* doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                                                      action:@selector(mapViewDoubleTapped:)];
doubleTapRecognizer.delegate = self;    // this allows
doubleTapRecognizer.numberOfTapsRequired = 2;
[singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];

然后实现:

#pragma mark UIGestureRecognizerDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer
*)otherGestureRecognizer {  return YES; }

-1
关于@Stanley的评论——
在UITableView中,不要尝试使用轻拍手势来使行选择起作用,因为它已经有了完整的轻拍处理......
但是,您必须将“取消视图中的触摸”设置为“否”才能让单击手势识别器获得轻拍事件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接