UIGestureRecognizers for single and double tap set in the xib - ios

I have set two UITapGestureRecognizers in my xib on a UIImageView. I have also set their IBAction in the associated header file.
For the single tap gesture recognizer, I set taps and touches to 1, state to Enabled, and delayed touches ended to YES in the Attributes inspector.
For the double tap gesture recognizer, I set taps and touches to 2, state to Enabled, cancel touches in view to YES and delay touches ended to YES.
When I double tap on the UIImageView, it only triggers the IBAction method for the single tap. So, I decided to print the imageview.gestureRecognizer and it shows the UITapGestureRecognizer for single tap's state as Ended and the UITapGestureRecognizer for double tap's state as Possible.
I have been stuck on this for a couple hours. I found ways to do it programatically but I was wondering how I can do it by setting it in the xib itself.
Any help would be great! Thank you in advance for your responses!

It's a very good question. If you add gestures to code like this
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget: self action:#selector(singleTap)];
singleTap.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:singleTap];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget: self action:#selector(doubleTap)] ;
doubleTap.numberOfTapsRequired = 2;
[self.view addGestureRecognizer:doubleTap];
[singleTap requireGestureRecognizerToFail:doubleTap];
And all works fine because you canceled first gesture here
[singleTap requireGestureRecognizerToFail:doubleTap];
If you add two gestures in xib you always should cancel single tap if there was a double tap. And you always need to use 2 properties for gestures and use
[self.firstGestureProperty requireGestureRecognizerToFail:self.secondGestureOroperty];
For single tap:
For double tap:
Source code:
And everything works fine.

Related

button action gets called before double tap gesture method

I've a scrollview on which one button is added, now I want to give action as well as double tap gesture to the button.
If the button is on UIView, both action of the button and double tap gesture methods work perfectly. But if the button is present on UIScrollView then action gets called followed by double tap gesture method.
Any help will be appreciated.
The scroll view needs a fair amount of touch logic to track, and that must be getting confused with the button's touch logic and your gesture recognizer.
Without investigating that further, I'd get around this by handling the button's taps via two gesture recognizers that you control, which should work no matter the parent view.
Do not give the button a target/action as you normally would in IB or in code
Create a single tap gesture recognizer to handle the button action.
Create a double tap gr as you probably do already. Add both to the button.
// for your current target-action behavior
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTap:)];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTap:)];
singleTap.numberOfTapsRequired = 1;
doubleTap.numberOfTapsRequired = 2;
[singleTap requireGestureRecognizerToFail:doubleTap];
[button addGestureRecognizer:singleTap];
[button addGestureRecognizer:doubleTap];
Then, instead of your IBAction method...
- (void)singleTap:(UIGestureRecognizer *)gr {
if (gr.state == UIGestureRecognizerStateRecognized) {
// target-action behavior here
NSLog(#"single tapped");
}
}
- (void)doubleTapped:(UIGestureRecognizer *)gr {
if (gr.state == UIGestureRecognizerStateRecognized) {
NSLog(#"double tapped");
}
}

How to add a UILongPressGestureRecognizer and UITapGestureRecognizer to the same control simultaneously?

It is similar to this question:
iPhone iOS how to add a UILongPressGestureRecognizer and UITapGestureRecognizer to the same control and prevent conflict?
but my problem is more complex.
I want to implement the same behavior you see in iOS 8 on iPad. I mean page grid in Safari.
The problem: one view should respond to both long press and tap gesture recognizers. The following things should work:
1)close button accepts clicks
2)when the tap begins the selected view should perform scale animation
3)on long press the selected view becomes draggable
If I don't use (requireGestureRecognizerToFail:) then tap gesture doesn't work. If I use this method then everything works but the long press events take place with huge delays.
How to solve this issue.
You need to use the requireGestureRecognizerToFail method.
//Single tap
UITapGestureRecognizer *tapDouble = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleTapGestureForSearch:)];
tapDouble.numberOfTapsRequired = 1;
tapDouble.delegate = self;
[self addGestureRecognizer:tapDouble];
//long press
UILongPressGestureRecognizer *longPressGestureRecognizer=[[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(handleLongPressRecognizer:)];
longPressGestureRecognizer.numberOfTouchesRequired=1;
longPressGestureRecognizer.minimumPressDuration = 0.5f;
[longPressGestureRecognizer requireGestureRecognizerToFail:tapDouble];
longPressGestureRecognizer.delegate = self;
[self addGestureRecognizer:longPressGestureRecognizer];
This means Long press gesture wait for the single Tap.
You can add time to the long press gesture.
UILongPressGestureRecognizer *longPressGesture=[[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(ontappLongPressGesture:)];
longPressGesture.minimumPressDuration=0.6;
longPressGesture.delegate=self;
[cell.view addGestureRecognizer:longPressGesture];
UITapGestureRecognizer *gesture=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(cellSelected:)];
//[gesture requireGestureRecognizerToFail:longPressGesture];
gesture.delegate=self;
[cell.view addGestureRecognizer:gesture];
also you need to set this delegate to work both gesture together
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}

UILongPressGestureRecognizer on UIButton not working

I have a longpress gesture recognizer that I create in ViewDidLoad then attach to a button like this, the button is created in the storyboard and linked to my class.
UILongPressGestureRecognizer *hold = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(secretChange:)];
hold.minimumPressDuration = 5.0;
hold.delegate = self;
[_button addGestureRecognizer:hold];
The class conforms to the GestureRecognizer protocol and I have my selector here
- (void)secretChange:(UILongPressGestureRecognizer *)sender {
// Some stuff
NSLog(#"Secret");
}
The selector is not being called and I cannot figure out why, this seems to be the code everyone gives out on the internet, I have tried removing the minimum duration to make sure I didn't accidentally set it ridiculously long
UPDATE: I am actually adding this gesture recognizer to multiple buttons like this
[_button1 addGestureRecognizer:hold];
[_button2 addGestureRecognizer:hold];
[_button3 addGestureRecognizer:hold];
What is happening is the gesture recognizer is only being applied to the last button I add it to. How do I get the gesture recognizer added to ALL the buttons? Do I need to make a new one for every button?
You should have three instance of UILongPressGestureRecognizer.
Before add a gesture recognizer to a new view, the addGestureRecognizer method will remove the gesture recognizer from the view it has been attached to.

Tap gesture recognizer on image throws error when passing event

I have a UIImageView inside of a custom cell. I am creating a custom tap gesture recognizer for when the UIImageView is tapped to load another detail view.
The tap gesture is set up like so:
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imageTapped:event:)];
tapRecognizer.cancelsTouchesInView = YES;
tapRecognizer.numberOfTapsRequired = 1;
tapRecognizer.delegate = (id)self;
[cell.userImage addGestureRecognizer:tapRecognizer];
cell.userImage.userInteractionEnabled = YES;
I am using imageTapped:event: so that I can detect what cell the user is tapping and load the data accordingly. The problem is I get this error:
If I get rid of event like so, its works perfectly with no issues.
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imageTapped:)];
I have used this method before in another application but for some reason I cannot get it to work properly with this error. Anyone recognize what this is? Thanks!
You need to read the documentation on UIGestureRecognizer...
A gesture recognizer has one or more target-action pairs associated
with it. If there are multiple target-action pairs, they are discrete,
and not cumulative. Recognition of a gesture results in the dispatch
of an action message to a target for each of those pairs. The action
methods invoked must conform to one of the following signatures:
- (void)handleGesture;
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer;
Using one of these signatures will prevent the crash as you have already observed. You could then access the image view that was involved in the gesture by inspecting the recognizer's view property, and from this you will be able to access the appropriate UITableView cell.

UIButton with both Touch Up Inside and Touch Repeat?

What's the best way to get a button to be capable of both a Touch Up Inside action and a Touch Down Repeat action? Kind of like the Home button on an iPhone: one click goes to the home screen, but two rapid clicks opens the multitasking bar. Right now my button has both methods hooked up, but the Touch Up Inside method (unsurprisingly) gets called before the Touch Down Repeat can happen.
I can come up with a few ways I might pull this off (having the Touch Up Inside method wait a second to see if another click comes before executing, having a second button move invisibly into place after the first click, etc) but they all seem kind of hack-y and open to performance losses and bugs. I found the tapCount property, but it's for UITouch instead of UIButton, so if([_buttonAddItems tapCount] > 1) {} else if([_buttonAddItems tapCount] == 1), which seems like it would be the most efficient way of doing it, doesn't work.
Is there a best-practice for this sort of thing? Or if not, does anyone have a preferred way of getting this done?
Try disconnecting both touch events from your button, and hook two tap gesture recognizers to it instead. Here is a link to an answer that explains how to set up two gesture recognizers so that one of them recognizes a single tap, and the other one recognizes a double tap:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget: self action:#selector(doSingleTap)];
singleTap.numberOfTapsRequired = 1;
[myButton addGestureRecognizer:singleTap];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget: self action:#selector(doDoubleTap)];
doubleTap.numberOfTapsRequired = 2;
[myButton addGestureRecognizer:doubleTap];
// This is the "secret sauce":
[singleTap requireGestureRecognizerToFail:doubleTap];

Resources