iPhone - gesture recognizers fighting each other - iphone

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).

Related

UITapGestureRecognizer for Single and Double Tap [duplicate]

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.

How to detect tap and double tap at same time using UITapGestureRecognizer?

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

iphone development : gesture recognition scrolling enabled simultaneously

In my app I need have a swipe gesture recogniser on my background scroller for the up direction. Here is my code below
It is in viewDidLoad
UISwipeGestureRecognizer *Swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(SwipeRecognizer:)];
Swipe.direction = UISwipeGestureRecognizerDirectionUp;
[backgroundScroller addGestureRecognizer:Swipe];
and it is the SwipeRecognizer:
- (void) SwipeRecognizer:(UISwipeGestureRecognizer *)sender {
if (sender.direction | UISwipeGestureRecognizerDirectionUp){
NSLog(#" *** SWIPE UP ***");
}
}
The problem is I cant enable scrolling and capture the gesture simultaneously. when I said scrolling is not enabled, I can recognise the gesture. But I need to to scrolling and gesture recognition simultaneously. isn't it possible?
Override the gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: method to don't block the UIScrollViews Pan recognizer
And it will work…
Dont forget to add delegate to self for gesture recogniser. As mentioned in #death7eater's comment.
I solved my problem like that:
This is for the viewDidLoad:
UISwipeGestureRecognizer *Swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(SwipeRecognizer:)];
Swipe.direction = UISwipeGestureRecognizerDirectionUp;
[backgroundScroller addGestureRecognizer:Swipe];
Swipe.delegate = self;
This is the SwipeRecognizer method:
- (void) SwipeRecognizer:(UISwipeGestureRecognizer *)sender {
if (sender.direction | UISwipeGestureRecognizerDirectionUp){
NSLog(#" *** SWIPE UP ***");
}
}
And thanks to #lukaswelte this allows to perform multiple gestures simultaneously:
- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}

Multiple Gestures dont seem to be working (*CustomGestureRecognizer and UILongPressGestureRecognizer)

I've made a custom gesture recognizer and im adding it to my customView .
The custom gesture is a subclass of UIPanGestureRecognizer .
The other gesture that im adding is LongPressGestureRecognizer
CustomGestureRecognizer *pan;
pan = [[CustomGestureRecognizer alloc] initWithTarget:[self viewController] action:#selector(dragImage:)];
[pan setDirection:DirectionPangestureRecognizerVertical];
[pan setMinimumNumberOfTouches:1];
[pan setMaximumNumberOfTouches:1];
[custom addSubview:custom.imageView];
[custom addGestureRecognizer:pan];
[pan release];
UILongPressGestureRecognizer *highLight = [[UILongPressGestureRecognizer alloc] initWithTarget:[self viewController]
action:#selector(highlightImage:)];
[highLight setDelaysTouchesBegan:0.1];
[tempView addGestureRecognizer:highLight];
[highLight release];
Also i have implemented
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if (![gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] && ![otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]])
{
return YES;
}
return YES;
}
Both im my [self viewController] and in the self class but im still not getting both the gesture to work simultaniiosly .
You forgot to set the delegates of your gesture recognizers...that method will never be called.

Add a UITapGestureRecognizer to a UIWebView

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