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
{
}
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)
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.
You need to use the requireGestureRecognizerToFail: method. Something like this:
[singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];
//----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];
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
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)
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;
}
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; }
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.
Related
I have a tutorial for my app, which should display only the first time the app is opened and should be tapped to dismiss.
I am initializing a UITapGestureRecognizer in my viewDidLoad:
tapper_tut = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
tapper_tut.cancelsTouchesInView = FALSE;
[self.view addGestureRecognizer:tapper_tut];
and I have an IBAction to detect the tap and set the tutorial to hidden:
- (IBAction)dismiss_tut{
if (????????????????) {
_tutorial.hidden = YES;
}
}
But I have no idea what to put in the if statement condition, or if this is even that right way to go about this.
How would I dismiss a UIImageView on a tap?
UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
[self.view addGestureRecognizer:gr];
// if not using ARC, you should [gr release];
// mySensitiveRect coords are in the coordinate system of self.view
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer {
CGPoint p = [gestureRecognizer locationInView:self.view];
if (CGRectContainsPoint(mySensitiveRect, p)) {
NSLog(#"got a tap in the region i care about");
} else {
NSLog(#"got a tap, but not where i need it");
}
}
You can make viewDidLoad like this
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
/* Create the Tap Gesture Recognizer */
self.tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleTaps:)];
/* The number of fingers that must be on the screen */
self.tapGestureRecognizer.numberOfTouchesRequired = 1;
/* The total number of taps to be performed before the gesture is recognized */
self.tapGestureRecognizer.numberOfTapsRequired = 1;
/* Add this gesture recognizer to the view */
[self.view addGestureRecognizer:self.tapGestureRecognizer];
}
To detect the taps you can make the method like this.
- (void) handleTaps:(UITapGestureRecognizer*)paramSender
{
NSUInteger touchCounter = 0;
for (touchCounter = 0;touchCounter < paramSender.numberOfTouchesRequired;touchCounter++)
{
CGPoint touchPoint =[paramSender locationOfTouch:touchCounter inView:paramSender.view];
NSLog(#"Touch #%lu: %#",(unsigned long)touchCounter+1, NSStringFromCGPoint(touchPoint));
}
}
you have to declare .h file as "UIGestureRecognizerDelegate"
you have getting tap of gesture as two way as given below steps.
1) Call delegate method of GestureRecognizer (not given action )
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:nil]; // not given action.
recognizer.numberOfTouchesRequired=1;// here how many tap you want set it
[self.view addGestureRecognizer:recognizer];
recognizer.delegate = self;
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
//whatever you want write code here
return NO;
}
2) given action
UITapGestureRecognizer *oneTouch=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(Addphoto)];
[oneTouch setNumberOfTouchesRequired:1];
[self.view addGestureRecognizer:oneTouch];
-(IBAction)Addphoto
{
//whatever you want write code here
}
may be it will help .
I think u need to detect the first time launch of the application which u can do with following
![[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedOnce"]
Put this in your if statement .
For example I have a view where I want two different gestures:
tap to do action A.
double tap to do action B.
The problem is with UITapGestureRecognizer I can only set minimum required tap count. The single tap gesture recognizer recognizes a tap before the double tap gesture recognizer recognizes the double tap.
Example:
_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGestureRecognized:)];
_tapGestureRecognizer.numberOfTouchesRequired = 1;
_tapGestureRecognizer.numberOfTapsRequired = 1;
[self addGestureRecognizer:_tapGestureRecognizer];
_doubleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTapGestureRecognized:)];
_doubleTapGestureRecognizer.numberOfTouchesRequired = 1;
_doubleTapGestureRecognizer.numberOfTapsRequired = 2;
[self addGestureRecognizer:_doubleTapGestureRecognizer];
It always recognizes the single tap even if I do double tap very fast. How can I set it up so the tap gesture recognizer waits and sees if the double tap gesture recognizer recognizes?
Here is what I have used in one of my old projects, I hope it helps you out man.
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];
On swift 3 and swift 4:
let singleTap = UITapGestureRecognizer(target: self, action: #selector(doSingleTap))
singleTap.numberOfTapsRequired = 1
self.view.addGestureRecognizer(singleTap)
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(doDoubleTap))
doubleTap.numberOfTapsRequired = 2
self.view.addGestureRecognizer(doubleTap)
singleTap.require(toFail: doubleTap)
{ 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];
[self.headerView addGestureRecognizer:singleTap];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
Create the gesture recognizers inside any method you want. For example if you want tap gestures for your headerView in tableView create those tap gestures inside
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
And your class should implement UIGestureRecognizerDelegate.
Above mentioned are the 2 methods of that delegate class.
Another option is to use UIGestureRecognizerDelegate, especially if you do not have easy access to the other recognizer.
let singleTap = UITapGestureRecognizer(target: self, action: #selector(doSingleTap))
singleTap.numberOfTapsRequired = 1
singleTap.delegate = self
self.view.addGestureRecognizer(singleTap)
Implement the delegate:
extension ViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if let tapGesture = gestureRecognizer as? UITapGestureRecognizer, let otherTapGesture = otherGestureRecognizer as? UITapGestureRecognizer {
return tapGesture.numberOfTapsRequired == 1 && otherTapGesture.numberOfTapsRequired == 2
}
return false
}
}
I found a solution to the delay of the single tap using XCode Monkey's answer. Using this approach, you can configure in mili-seconds the threshold between a single and a double tap. This technique uses a simple UIButton and a NSTimer to differentiate between single and double tap.
#property NSTimer *tapTimer;
#property BOOL isDoubleTap;
UIButton *yourButton = [UIButton new];
yourButton.frame = // set your frame
[yourButton addTarget:self action:#selector(handleTap:) forControlEvents:UIControlEventTouchUpInside];
[self.view yourButton];
-(void)handleTap:(UIButton*)button
{
[self.tapTimer invalidate];
if (self.isDoubleTap)
{
[self doDoubleTap];
return;
}
self.isDoubleTap = YES;
// CHANGE THE 0.3 SECOND THRESHOLD BETWEEN SINGLE/DOUBLE TAP TO YOUR HEART'S CONTENT
self.tapTimer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:#selector(doSingleTap) userInfo:nil repeats:NO];
}
-(void)doSingleTap
{
self.isDoubleTap = NO;
}
-(void)doDoubleTap
{
self.isDoubleTap = NO;
}
tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleTapGesture:)];
tapGesture.numberOfTapsRequired = 2;
tapGesture.numberOfTouchesRequired = 1;
[self.view addGestureRecognizer:tapGesture];
[tapGesture release];
and
- (void)handleTapGesture:(UITapGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateRecognized) {
// handling code
NSLog(#"We got double tap here");
DashBoardViewController* dashboardObj = [[DashBoardViewController alloc] initWithNibName:#"DashBoardViewController" bundle:nil];
[self.navigationController pushViewController:dashboardObj animated:YES];
}
what i am trying to do is , i want to call 2 different events on single tap and on double tap.
So how can i detect when tap==1 and tap==2?
Double tap is recognised in my code, but i am not sure, how to find and work,when a single tap is find.
Thanks
This may give u a soln
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTap:)];
[doubleTap setNumberOfTapsRequired:2];
[singleTap setNumberOfTouchesRequired:1];
[self.view addGestureRecognizer:singleTap];
[self.view addGestureRecognizer:doubleTap];
- (void)handleSingleTap:(UIGestureRecognizer *)gestureRecognizer {
// single tap action
}
- (void)handleDoubleTap:(UIGestureRecognizer *)gestureRecognizer {
// double tap action
}
or u have to use NSTimer as darren pointed to validate the single touch.
In the method,
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
Following seems to a good post about touch in iOS.
http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/
In iOS, is it possible to put a tap recognizer on a UIWebView, so that when someone single-taps the web view, an action gets performed?
Code below doesn't seem fire handleTap when I tap my webView.
Thanks.
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleTap)];
tap.numberOfTapsRequired = 1;
[webView addGestureRecognizer:tap];
[tap release];
-(void) handleTap {
NSLog(#"tap");
}
I found that I had to implement this method to get it to work:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
Your UIViewController subclass should implement UIGestureRecognizerDelegate and return YES from gestureRecognizer: shouldReceiveTouch: when appropriate.
Then you can assign it to the delegate property of your UIGestureRecognizer.
tap.delegate = self;
Add UIGestureRecognizerDelegate to view controller
<UIGestureRecognizerDelegate>
Add Tap Gesture to Webview.
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(flip)];
tap.numberOfTapsRequired = 1;
tap.delegate = self;
[_webview addGestureRecognizer:tap];
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
I have added two gesture recognizers to a view. One will handle the view drag and the other the double tap. Something like
UITapGestureRecognizer *doubleTap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(deleteThisView)];
[doubleTap setDelegate:self];
[doubleTap setCancelsTouchesInView:YES];
[doubleTap setNumberOfTapsRequired:1];
[base addGestureRecognizer:doubleTap];
[doubleTap release];
UIPanGestureRecognizer *panGesture = nil;
panGesture = [[UIPanGestureRecognizer alloc]
initWithTarget:self action:#selector(drag:)];
[panGesture setMaximumNumberOfTouches:1];
[panGesture setDelegate:self];
[base addGestureRecognizer:panGesture];
[panGesture release];
The problem is this: as the view can move, the double tap is somehow difficult to obtain, because the view can slide one hair to one side or the other and iOS will not recognize it as double tap, and instead will see it as two moves and the drag method will run twice.
I am not seeing how this can be done. Any clues?
thanks
There's a delegate method that will tell the central gesture code that two recognizers might be going at the same time:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if (gestureRecognizer==_panRecognizer && otherGestureRecognizer==_swipeRecognizer)
return YES;
if (gestureRecognizer==_swipeRecognizer && otherGestureRecognizer==_panRecognizer)
return YES;
return NO;
}
Maybe you can try something like that.
Here is Vagrant's answer in swift:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer == panRecognizer && otherGestureRecognizer == swipeRecognizer {
return true
}
if gestureRecognizer == swipeRecognizer && otherGestureRecognizer == panRecognizer {
return true
}
return false
}
Don't forget to implement the delegate protocol and set your gestures' delegates to self (I forgot this initially).