Thursday, October 2, 2008

Handling Double Taps

Alright, since the NDA is down, let's celebrate by posting some information, shall we?

One question that I see posted a lot by new iPhone developers is "how do I handle double-taps?" The problem is that a double tap calls touchesBegan:withEvent: twice. If you want to have a different method called for a single-tap then a double-tap (in other words, if there's a double-tap, you don't want the single-tap method to fire), how do you handle it?

The answer lies in first using performSelector:withObject:afterDelay: to call the single-tap method after a delay that's short enough not to be noticed by the user, but long enough that the second tap of the double-tap will arrive before it actually executes. Then when the double-tap comes into touchesBegan:withEvent:, you use a little-known NSObject method called cancelPreviousPerformRequestsWithTarget:selector:object: to cancel the previous single-tap method call before calling the double-tap method.

Heres a code snippet that shows how to handle up to a quadruple-tap, with each number of taps resulting in a different method getting called.


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
NSUInteger tapCount = [touch tapCount];

switch (tapCount) {
case 1:
[self performSelector:@selector(singleTapMethod) withObject:nil afterDelay:.4];
break;
case 2:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(singleTapMethod) object:nil];
[self performSelector:@selector(doubleTapMethod) withObject:nil afterDelay:.4];
break;
case 3:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(doubleTapMethod) object:nil];
[self performSelector:@selector(tripleTapMethod) withObject:nil afterDelay:.4];
break;
case 4:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(tripleTapMethod) object:nil];
[self quadrupleTap];
break;
default:
break;
}

}
@end




7 comments:

phil swenson said...

Good post. I'd like to see how to handle taps (one im my case) on a UIWebView..... I'm stumped on this.

Jeff LaMarche said...

Phil - I'll do a post on this...

cirineu said...

Perfect!!! I had this problem, all the time my double tap was triggering my single tap method! Thanks a lot!

iVinay said...

great that works fine!! solved my problem.. thanks

Rudif said...

Hi Jeff

I want to have a singleTap and a doubleTap action on a button. Investigating, I tried your logic in this post, as well as logic suggested in Apple doc.

On simulator, SDK 3.0 - in the case of double tap the button receives twice in a row the touchesBegan event with tap count of 2 - no idea why.

Your logic (as reinterpreted by me) produces 2 calls to -doubleTap while Apple logic produces just one call, as appropriate.

Do you have any comments on this?

Below is my code, from a simple view-based app.

Rudi

PS : what is the trick for preserving code indentation in posts?


// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button1 setTitle:@"Button 1" forState:UIControlStateNormal];
[button1 sizeToFit];
button1.center = self.view.center;
[button1 addTarget:self action:@selector(action1:forEvent:) forControlEvents:UIControlEventAllEvents];
[self.view addSubview:button1];
}

- (void)action1:(id)sender forEvent:(UIEvent *)event {
NSSet *touches = [event allTouches];
UITouch* touch = [touches anyObject];
NSUInteger tapCount = [touch tapCount];
NSUInteger phase = [touch phase];
NSLog(@"%s : tapCount=%d phase=%d %@", __FUNCTION__, tapCount, phase, [self strPhase:phase]);
#ifdef BEFORE
// ref http://iphonedevelopment.blogspot.com/2008/10/handling-double-taps.html
if (phase == UITouchPhaseBegan) {
switch (tapCount) {
case 1:
[self performSelector:@selector(singleTapMethod) withObject:nil afterDelay:0.3];
break;
case 2:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(singleTapMethod) object:nil];
[self doubleTapMethod];
break;
default:
break;
}
}
// above sees 2 x : tapCount=2 phase=0 and so calls the doubleTapMethod twice
#else
// ref http://developer.apple.com/IPhone/library/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/EventHandling/EventHandling.html#//apple_ref/doc/uid/TP40007072-CH9
if (phase == UITouchPhaseEnded && tapCount == 1) {
[self performSelector:@selector(singleTapMethod) withObject:nil afterDelay:0.3];
}
else if (phase == UITouchPhaseBegan && tapCount == 2) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(singleTapMethod) object:nil];
}
else if (phase == UITouchPhaseEnded && tapCount == 2) {
[self doubleTapMethod];
}
// above works
#endif
}

-(void) singleTapMethod {
NSLog(@"%s : ", __FUNCTION__);
}

-(void) doubleTapMethod {
NSLog(@"%s : ", __FUNCTION__);
}

Edwin said...

scrub m65 kamagra attorney lawyer body scrub field jacket lovegra marijuana attorney injury lawyer

h4ns said...

What youre saying is completely true. I know that everybody must say the same thing, but I just think that you put it in a way that everyone can understand. I also love the images you put in here. They fit so well with what youre trying to say. Im sure youll reach so many people with what youve got to say.

Arsenal vs Huddersfield Town live streaming
Arsenal vs Huddersfield Town live streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Notts County vs Manchester City Live Streaming
Notts County vs Manchester City Live Streaming
Bologna vs AS Roma Live Streaming
Bologna vs AS Roma Live Streaming
Juventus vs Udinese Live Streaming
Juventus vs Udinese Live Streaming
Napoli vs Sampdoria Live Streaming
Napoli vs Sampdoria Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
AS Monaco vs Marseille Live Streaming
AS Monaco vs Marseille Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Technology News | News Today | Live Streaming TV Channels