UITapGestureRecognizer - single tap and double tap - ios

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

Allow both single tap gesture recognizer and double tap in UIScrollView

So I have an UIImageView inside of a UIScrollView, I need to capture both:
- single tap gesture
- double tap gesture
for the UIImageView
The double tap gesture is supposed to be sent to the UIScrollView as an action of zooming
The single tap gesture is captured by a UITapGestureRecognizer that I created myself.
The double tap gesture has higher priority than the single tap (when there is a double tap, zoom in the scrollview, else perform the single tap)
So far if I add the single tap gesture recognizer to the scrollview, the single tap is immediately recognized and no double tap can be recognized.
If I add the single tap gesture recognizer to the imageview, it never receives any action, but double tap works
Any suggestion is appreciated, thanks..
try this code,
Objective - C
UITapGestureRecognizer *singletap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(singleTap:)];
singletap.numberOfTapsRequired = 1;
singletap.numberOfTouchesRequired = 1;
[scrollView addGestureRecognizer:singletap];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(doubleTap:)];
doubleTap.numberOfTapsRequired =2 ;
doubleTap.numberOfTouchesRequired = 1;
[scrollView addGestureRecognizer:doubleTap];
[singleTap requireGestureRecognizerToFail:doubleTap];
Swift 3.0
var singletap = UITapGestureRecognizer(target: self, action: #selector(self.singleTap))
singletap.numberOfTapsRequired = 1
singletap.numberOfTouchesRequired = 1
scrollView.addGestureRecognizer(singletap)
var doubleTap = UITapGestureRecognizer(target: self, action: #selector(self.doubleTap))
doubleTap.numberOfTapsRequired = 2
doubleTap.numberOfTouchesRequired = 1
scrollView.addGestureRecognizer(doubleTap)
singleTap.require(toFail: doubleTap)
UITapGestureRecognizer *tapRecognizerForSingleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapAction:)];
[tapRecognizerForSingleTap setNumberOfTapsRequired:1];
UITapGestureRecognizer *tapRecognizerForDoubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTapAction:)];
[tapRecognizerForDoubleTap setNumberOfTapsRequired:2];
Both are added in a same image view and to achieve what you want just add the below line.
[tapRecognizerForSingleTap requireGestureRecognizerToFail:tapRecognizerForDoubleTap];
By the above line you are asking for single tap will be only recognise if it's not double tap.

Single Tap TapGestureRecognizer

I am trying to use this code for a tap gesture recognizer, it works fine when number of taps required is set to 2, but when I set the number of taps required to 1 it stop functioning. I appreciate any help in getting this to work.
UITapGestureRecognizer *doubleTap =
[[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(tapDetected:)];
[doubleTap setNumberOfTapsRequired : 1];
[doubleTap setDelaysTouchesBegan : YES];
[self.view addGestureRecognizer:doubleTap];
You try this:-
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget: self action:#selector(tapDetected:)];
singleTap.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:singleTap];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget: self action:#selector(tapDetected:)];
doubleTap.numberOfTapsRequired = 2;
[self.view addGestureRecognizer:doubleTap];
[singleTap requireGestureRecognizerToFail:doubleTap];
This works for me.
If You only want the receiver to respond to one/first touch then setDelaysTouchesBegan:NO Since this is used for processing touches in UITouchPhaseBegan so it is analyzed and is prevented from being delivered. Property discussion from documentation:
When the value of this property is NO (the default), views analyze touch events in UITouchPhaseBegan and UITouchPhaseMoved in parallel with the receiver. When the value of the property is YES, the window suspends delivery of touch objects in the UITouchPhaseBegan phase to the view. If the gesture recognizer subsequently recognizes its gesture, these touch objects are discarded. If the gesture recognizer, however, does not recognize its gesture, the window delivers these objects to the view in a touchesBegan:withEvent: message (and possibly a follow-up touchesMoved:withEvent: message to inform it of the touches’ current locations). Set this property to YES to prevent views from processing any touches in the UITouchPhaseBegan phase that may be recognized as part of this gesture.
It looks to me like you are simply failing to set the delegate of your GestureRecognizer. Are you sure the double tap was firing successfully? When I placed your code into my project I see the same behavior but setting the delegate properly and using shouldRecognizeSimultaneouslyWithGestureRecognizer causes it to recognize the single tap properly.
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(tapDetected:)];
[doubleTap setDelegate:self];
[doubleTap setNumberOfTapsRequired : 1];
[doubleTap setDelaysTouchesBegan : YES];
[picker addGestureRecognizer:doubleTap];
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([gestureRecognizer isKindOfClass:UITapGestureRecognizer.class] &&
[otherGestureRecognizer isKindOfClass:UITapGestureRecognizer.class])
{
return YES;
}
else
{
return NO;
}
}

UIGestureRecognizer and UIControlEventTouchUpInside work at the same time

I am adding a UITapGestureRecognizer to my application to detect a double tap gesture.
UITapGestureRecognizer *tapgr = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleTapGesture:)];
tapgr.numberOfTaps = 2;
tapgr.delegate = self;
[_view addGestureRecognizer:tapgr];
[tapgr release];
This is working fine unless I show a tooltip in my application. They are set up like this:
[_view.toolTipView addTarget:self action:#selector(handleTap:) forControlEvents:UIControlEventTouchUpInside];
Before the introduction of my gesture recognizer, these tooltips where clickable and reacted, now they aren't reacting anymore...
How can I make a gesture recognizer and a standard UIControlEventTouchUpInside-Setup work together?
I found a solution:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if (gestureRecognizer == tapgr) {
return ![touch.view isKindOfClass:[UIControl class]];
}
return YES;
}
This method prevents the GestureRecognizer from being fired when a UIControl (i.e. Button) is pressed.
if you want the _view can respond single tap and Double tap together, you can try this
UITapGestureRecognizer * singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
UITapGestureRecognizer * doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTap:)];
[doubleTap setNumberOfTapsRequired:2];
//Add this code to disable double tap and only responds to single tap when user tap the _view just once time
[singleTap requireGestureRecognizerToFail:doubleTap];
[self addGestureRecognizer:doubleTap];
[self addGestureRecognizer:singleTap];
[singleTap release];
[doubleTap release];
if you want to use TouchEvent try UIControlEventTouchDownRepeat
[button addTarget:self actionselector(multipleTap:withEvent:) forControlEvents:UIControlEventTouchDownRepeat];
//Try to check wheather is doulbe tap or single tap.
-(IBAction)multipleTap:(id)sender withEvent:(UIEvent*)event {
UITouch* touch = [[event allTouches] anyObject];
if (touch.tapCount == 2) {
// do action.
}
}
You can't do that. I think you should choose another way to implement that.

Implement singleTap and doubleTap in Objective-C

I add singTap and doubleTap to a view like the code below:
-(void)awakeFromNib{
[self setUserInteractionEnabled:YES];
//
UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTapGesture:)];
doubleTapGesture.numberOfTapsRequired = 2;
[self addGestureRecognizer:doubleTapGesture];
UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTapGesture:)];
singleTapGesture.numberOfTapsRequired = 1;
[self addGestureRecognizer:singleTapGesture];
}
-(void)handleSingleTapGesture:(UITapGestureRecognizer *)singleTapGesture{
[[self delegate] singleTapOnView];
}
-(void)handleDoubleTapGesture:(UITapGestureRecognizer *)doubleTapGesture{
[[self delegate] doubleTapOnView];
}
When doubleTap the singleTap also fire. How to disable singleTap when doubleTap? Thanks!
Tell the single-tap recognizer that it requires the double-tap recognizer to fail before it triggers:
[singleTapGesture requireGestureRecognizerToFail:doubleTapGesture];
(This is actually the canonical example in the UIGestureRecognizer docs for this method.)

Mutiple gesture recognizers on same UIImage

I am trying to add 2 gestures to a uiimage that pulls a subVIew. One gesture is swipe to open/close. And the other is longpressand hold to open it at a specific height.
longPress.minimumPressDuration = 1;
[imageNavigationSlider addGestureRecognizer:longPress];
UILongPressGestureRecognizer *longPressWall = [[[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleLongPressWall:)] autorelease];
longPressWall.minimumPressDuration = 1;
PullMeImage.userInteractionEnabled=YES;
[PullMeImage addGestureRecognizer:longPressWall];
UISwipeGestureRecognizer *swipe = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipe:)] autorelease];
swipe.direction = UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight;
[PullMeImage addGestureRecognizer:swipe];
[swipe setDelegate:self];
The long press gesture is correctly recognized, but the swipe isn't.
I also tried using:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
But with no success. Any help will be very much appreciated.
Thank you

Resources