I have a gesture recognizer that I've made on a calculator. It's connected to an action that is activated upon a user's double tap. I connected the gesture recognizer to the main view of the view controller, however the gesture recognizer is also applied to my buttons. So if the user quickly types 11, they'll press 1 two times fast and accidentally activate a function that they don't want to. How do I make it so that the UIGestureRecognizer doesn't act upon buttons? I want to keep the double-tap gesture. I don't want to change it to a two finger tap or something odd like that. However, if there's no other way (which I doubt there is), I could do that.
Try this:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ((touch.view == yourButton)) {
return NO;
}
return YES;
}
This will be called every time the gesture is recognized, and it will ignore the gesture is the view is your button.
You should adopt UIGestureRegconizerDelegate protocol in your ViewController.h
#interface ViewController : UIViewController <UIGestureRecognizerDelegate>
#end
in your ViewController.m, implement this method
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([touch.view isKindOfClass:[UIControl class]]){
return NO;
}
return YES;
}
and yourGestureRecognizer.delegate = self //your view controller.
Building on Antonio's answer, you could give all of your tags to make this easier. Presumably, your calculator has more than just one yourButton. Suppose all your buttons have a tag larger than 100:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch {
if ((touch.view.tag > 100)) {
return NO;
}
return YES;
}
This is cleaner than checking the class of the view. You could now have controls where you allow the gesture to be recognised anyway.
Related
I have a button and a gesture recognition and when the button is pressed only one method should be called.
Most cases only the gesture recognition is being called which is fine but every no and then the button gets called to which causes problems.
I have a view and then a scroll view. All my buttons are on the scroll view
you can implement this in your view controller and assign it to the delegate of the gesture recognizer.
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
CGPoint p = [gestureRecognizer locationInView:self.view];
UIView *view = [sefl.view hitTest:p withEvent:nil];
if ([view isKindOfClass:[UIButton class]]) {
return NO;
}
return YES;
}
or you can implement - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch with similar logic if gestureRecognizerShouldBegin: does not suit.
We're building an app that takes advantage of the new UICollectionView in iOS 6. However, we need to implement a long-press behavior such that even if the user then moves their finger after, we want it ignored.
i.e.
User touches the screen than instantly moves -> Swipe
User touches the screen, pauses, then moves -> Ignore swipe and wait for the release.
Basically, we only want to allow the built-in swipe gesture to be enabled if our gesture recognizer fails. However, we're not sure how to supersede the built-in swipe gesture recognizers with that 'Other recognizer must fail first' logic.
We're not sure if we're allowed to simply walk the chain trying to find the UIScrollView and interrogate that as we don't know if that violates Apple's guidelines, and if I remember correctly, they actually warn against messing with their recognizers anyway.
So how can we create tap-to-hold recognizers that supersede the built in ones?
There's an example on page 36 of the UICollectionView programming guide:
UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapGesture:)];
NSArray* recognizers = [self.collectionView gestureRecognizers];
// Make the default gesture recognizer wait until the custom one fails.
for (UIGestureRecognizer* aRecognizer in recognizers) {
if ([aRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
[aRecognizer requireGestureRecognizerToFail:tapGesture];
}
}
// Now add the gesture recognizer to the collection view.
tapGesture.numberOfTapsRequired = 2;
[self.collectionView addGestureRecognizer:tapGesture];
Original answer
Have a look at UITapGestureRecognizerDelegate, which can be used to allow two gesture recognizers to process touches at once:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
For more info, see a tutorial such as this:
http://www.raywenderlich.com/6567/uigesturerecognizer-tutorial-in-ios-5-pinches-pans-and-more
Before your new UILongPressGestureRecognizer transnitions from the possible state, he will ask its delegate gestureRecognizerShouldBegin:. You can use that delegate method to cancel (force to failed state) any other gesture recogniser attached to the same view.
You do this by implementing the following as a delegate for your new UILongPressGestureRecognizer:
#implementation DragPictogramGestureRecognizerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
for (UIGestureRecognizer *gr in gestureRecognizer.view.gestureRecognizers) {
if ([gr isKindOfClass:[UILongPressGestureRecognizer class]] == NO) {
gr.enabled = NO;
gr.enabled = YES;
}
}
return YES;
}
#end
Further more, in order to allow the user to use your new UILongPressGestureRecognizer with one finger, and use another finger to scroll the UICollectionView at the same time, you can implement the following delegate in the same class.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
For example, I have a button, when click this button, it call an action: doAction1.
And an View: when touch in it, it call an action doAction2.
When clicked in this button, I touched in view, too.
Is it have any property to set priority to call doAction1 or doAction2?
I assume that you you put a Tapgesturerecogniyer to container view.You can ignore the tap gesture if user has tapped on the button.Just overwrite the fooling method
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// test if touched view is unbutton
if ([touch.view isKindOfClass:[UIButton class]])
{
return NO; // ignore the touch
}
return YES;
}
I have a UIPageViewController and each Page contains multiple UIViews on the page, each UIView allows Pan gesture after a longPress gesture, I use the following delegate in the contentVC, but it appears the pageVC doesn't care my restriction below and it continue to flip the page! do I have to do anything to disable the page turning while I am in panning? (the last resort will be setting an variable at the PageVC like a canTurn, and set it to NO while the panning is in action....
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] &&
[otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
return YES;
}
return NO;
}
You should add the -gestureRecognizer:shouldReceiveTouch: method in the PageVC itself, or if you dont wanna add it in the PageVC itself you could let a delegate handle it
Like:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if(someCase)
{
return yes;
}
return no;
}
or
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
return [delegate shouldHandle:.....];
}
I have added Tap Gesture recognizer to a view. My view has an image and a UIToolBar at the bottom with a few UIBarbuttons I want to cancel any touches on these buttons. I am trying to use the following method to cancel the touch. How do I detect whether the touch is on the toolbar or any bar buttons? Frame is also not defined for Bar buttons...
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (gestureRecognizer == tapRecognizer) {
if (touch.view==barbutton/*toolbar or bar button item*/)
{
return NO;
}
}
return YES;
}
CGPoint location = [touch locationInView:self.view];
if(CGRectContainsPoint(toolbar.frame, location)) { ... }
This is assuming the toolbar and self.view are in the same coordinate space. If not, you'll have to use UIView's coordinate conversion methods (convertPoint:toView:) to make the spaces match.
Buttons are the first responder and their uitouchup or other event will fire first and won't propogate to the backing view.
You can subclass your buttons and have the touchesbegan/moved/ended do:
[self.nextResponder touchesBegan:touches withEvent:event];
to have your backing view handle all their events for them in which case your gesture code should work.