161

I am trying to add 2 UITapGestureRecognizers to a view, one for single tap and one for double tap events. The single tap recognizer is working as expected (on its own). But I don't seem to be able to get the double tap recognizer working.

Have tried to experiment with properties like : cancelsTouchesInView, delaysTouchesBegan and delaysTouchesEnded but still doesn't work.

When I double tap, the single tap recognizer would always be activated and the double tap event would also be sent to the super view. But the custom double tap recognizer does not seem to be notified at all.

Documentations seem to suggest that the 3 properties mentioned above could be used for the purpose. But I am just not sure what values should be set and on which recognizer(s) (single, double or both). Hope somebody familiar with this could help.

The following is the latest updated code block.

// ****** 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
{

}
TheNeil
  • 1,985
  • 2
  • 17
  • 36
Stanley
  • 4,292
  • 6
  • 28
  • 47
  • 30
    Random comment: underscores in variable names will make most Objective-C programmers cringe. What's worse is that your variable names are not descriptive. Why not just use singleTapRecognizer and doubleTapRecognizer (or something similar)? It takes a split second more to type, but makes your code infinitely more readable. – UIAdam Jan 16 '12 at 07:16
  • Thanks for the advice, agree with you that the variable names should be more descriptive. But this particular code segment has not been finalized and will post more polished and descriptive code as suggested ... – Stanley Jan 16 '12 at 08:45
  • In case anyone else has a similar problem. I struggled to get double tap and single tap recognizers working together in swift (single tap was working fine, but no double tap). I dragged the recognizers onto the scene using storyboard. I had the handlers and the statement: singleTapRecognizer.requireGestureRecognizerToFail(doubleTabRecognizer) inside my viewDidLoad, but still no double tap. My missing piece of the puzzle was to drag the double tap recognizer icon (from the top row of the scene) over the portion of the view where the double tap was to be recognized. – Paulus Jun 17 '16 at 06:17
  • 1
    @UIAdam using an underscore to prefix a private ivar name in objc is fairly common though no? (coming from a swift background) – Charlton Provatas Aug 04 '17 at 15:43

9 Answers9

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

Note: If you are using numberOfTouchesRequired it has to be .numberOfTouchesRequired = 1;

For 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)
TheNeil
  • 1,985
  • 2
  • 17
  • 36
Anil Kothari
  • 7,433
  • 4
  • 17
  • 24
  • Thanks for the answer, have tried your code but it's still not working. There must still be something I'd missed. Do you have any more hint ? – Stanley Jan 16 '12 at 06:27
  • @Stanley: Anils answer should work, off course you also have to provide handlers (doSingleTap, doDoubleTap) – Rok Jarc Jan 16 '12 at 09:32
  • The single tap updates a UITableView which is working as expected. The double tap, for the time being, just output a log statement using NSLog. My suspicion is that the first responder status goes from the UIView to somewhere else after the first tap as I haven't done anything explicitly about the response chain. – Stanley Jan 17 '12 at 10:53
  • The solution provided by @Anil does work. In my case, some super views may be blocking the double taps while letting the single taps go through ... – Stanley Jan 29 '12 at 23:01
  • 8
    The solution works for me. The single tap selector is fired after some delay. – Vladimir Obrizan Jul 27 '12 at 05:39
  • You might performing some time consuming operation on single tap. Check – Anil Kothari Aug 01 '12 at 06:37
  • It might not work if you are trying to handle the events in the same handler, i was trying to distinguish them with numberOfTouches and didn't worked. – Cristi Băluță Sep 22 '13 at 14:02
49

Swift 3 solution:

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)

In the code line singleTap.require(toFail: doubleTap) we are forcing the single tap to wait and ensure that the tap event is not a double tap.

31

You need to use the requireGestureRecognizerToFail: method. Something like this:

[singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];

Chris Knadler
  • 2,761
  • 2
  • 23
  • 32
UIAdam
  • 5,293
  • 1
  • 25
  • 23
  • Have tried to use the "Fail" method call on with both recognizers just now again. But still doesn't work. If you have got the double tap thing working before, please share some more of your experience with me. – Stanley Jan 16 '12 at 06:13
  • 1
    I have tried this exact code (same as what Anil has posted in more detail) and it works for me. – UIAdam Jan 16 '12 at 06:20
  • Thanks for the comment, then there must still be something that I'd missed on my part ... – Stanley Jan 16 '12 at 06:34
  • Your code still messes with delaysTouches and whatnot. Make it so that it follows Anil's code exactly. Only use requireGestureRecognizerToFail once the way we have it. – UIAdam Jan 16 '12 at 07:10
  • Will do as you advised later today or tomorrow as I am running out of time for the moment. Thanks for your kind assistance ... – Stanley Jan 16 '12 at 08:55
  • Have fashioned the code block more closely with the one from Anil. Most cryptic variable names have been lengthened. Also, about the underscores, they are used just to differentiate custom identifiers from those of the Cocoa in library. Please excuse me if they look a bit out of place. – Stanley Jan 17 '12 at 09:42
  • As for the outcome, @Adam W, it's frustrating to say that the double tap is still not being registered. The interesting part is if I change the numberOfTouchesRequired from 2 to 1, the double tap recognizer gets activated. I think the problem in my case could be related to the responder chain. The UIView where the recognizers are attached to is created from code (not with IB), and I have not explicitly set it as a first responder. So, could the first responder status go form the UIView to somewhere else after the first tap ? – Stanley Jan 17 '12 at 10:06
15

//----firstly you have to alloc the double and single tap gesture-------//

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

//-----------------------number of tap----------------//

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

//------- add double tap and single tap gesture on the view or button--------//

[self.view addGestureRecognizer : doubleTap];
[self.view addGestureRecognizer : singleTap];
commanda
  • 4,772
  • 1
  • 23
  • 31
Jaspreet Singh
  • 1,168
  • 1
  • 12
  • 29
10

Not sure if that's exactly what are you looking for, but I did single/double taps without gesture recognizers. I'm using it in a UITableView, so I used that code in the didSelectRowAtIndexPath method

    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;

Methods singleTap and doubleTap are just void with NSIndexPath as a parameter:

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

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

Hope it helps

Novarg
  • 7,350
  • 2
  • 34
  • 72
  • Thanks for the answer, @Novarg. Did you have the problem I am having about the doubleTap recognizers ? – Stanley Jan 17 '12 at 10:16
  • @Stanley nope, the singleTap is waiting 0.2 seconds before firing. And if in the mean time the second tap was activated, then singleTap method is cancelled. – Novarg Jan 17 '12 at 10:26
  • It looks very promising, thanks for your assistance. Am running out of time today again. Will certainly give it a try tomorrow :) – Stanley Jan 17 '12 at 10:39
  • Creative answer ;) – Michael Jul 14 '16 at 19:28
  • very good solution. I just needed to add tapCount=0 on each handler (singleTap, doubleTap) in order to be sure that following touches are recognized as well. – Sirio Aug 20 '16 at 14:31
3

Solution for 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)
limfinity
  • 676
  • 6
  • 16
1

I implemented UIGestureRecognizerDelegate methods to detect both singleTap and doubleTap.

Just do this .

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

Then implement these delegate methods.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
    return  YES;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    return YES;
}
iPhoneDeveloper
  • 768
  • 1
  • 10
  • 22
0

Some view have there own double tap recognizers built in (MKMapView being an example). To get around this you will need to implement UIGestureRecognizerDelegate method shouldRecognizeSimultaneouslyWithGestureRecognizer and return YES:

First implement your double and single recognizers:

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

And then implement:

#pragma mark UIGestureRecognizerDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer
*)otherGestureRecognizer {  return YES; }
zerodog
  • 559
  • 4
  • 11
-1

In reference to @stanley's comment -

Don't try and use tap gestures to row selections to work in UITableView as it already has full tap handling....

But you must set 'Cancels Touches in View' to 'NO' on your single tap gesture recognizers or it will never get the tap events.

MarmiK
  • 5,320
  • 6
  • 34
  • 44
Oliver Dungey
  • 1,459
  • 18
  • 19