I have a main window xib on an iPad. The main window has tap gesture recognizers that show/hide a toolbar when the user taps the screen. There is also a web view in the main window.
How would I go about canceling the gestures if the user clicks a link in the web view? I don't want the toolbar to toggle if they hit a link.
Thanks
You need to check if the view should receive the touch implementing:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// test if our control subview is on-screen
if (self.view.superview != nil) {
if ([touch.view isKindOfClass:[UIWebView class]]) {
// we touched our UIWebView
return NO; // ignore the touch
}
}
return YES; // handle the touch
}
If you want to use the UITapGestureRecognizer you need to subclass the UIWebView as explained here: https://github.com/psychs/iphone-samples/tree/4028ab78af92ab17465338575b78ed80310a613f/WebViewTappingHack
Related
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.
I created a UIPanGestureRecognizer in a UICollectionViewCell subclass. It works in all cells except the top of the screen. About the height of status bar the pan gesture recognizer is dead. It doesn't fire. My test app only has a UICollectionView and nothing else. No status bar. I think maybe it has to do with the notification center "active edge" stealing touch events.
_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panGestureRecognized:)];
_panGestureRecognizer.delegate = self;
_panGestureRecognizer.maximumNumberOfTouches = 1;
[self addGestureRecognizer:_panGestureRecognizer];
At the top of screen when I pan in the cell I get no message upon pan begin. There is no call to the callback until I lift the finger. Then the callback receives BEGAN and immediately after that it receives ENDED.
- (void)panGestureRecognized:(UIPanGestureRecognizer*)sender {
NSLog(#"pan detected"); // for first cell, only called when finger is lifted up.
if (sender.state == UIGestureRecognizerStateBegan) {
NSLog(#"BEGAN");
} else if (sender.state == UIGestureRecognizerStateEnded) {
NSLog(#"ENDED");
}
}
The cell is the delegate of the gesture recognizer. It implements:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
The cell also overwrites the UIView method:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
return YES;
}
Is this a known problem with pan gesture recognizers at the top of the screen? And is there a workaround?
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 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.