Previously I've used the tap Detecting Window technique to detect taps in a UIWebView, but now I've tried to use gesture recognizers instead. The following code is in the viewDidLoad method of a view controller, which has a single UIWebView. This code compiles fine, but the handleTap method is never called. This seems like it should be simple.
// Configure a gesture recognizer to detect taps in the web view
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap)];
[self.myWebView addGestureRecognizer:singleTap];
[singleTap release];
[super viewDidLoad];
Set your view controller as the recognizer delegate:
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap)];
recognizer.delegate = self;
[self.myWebView addGestureRecognizer:recognizer];
And enable simultaneous gesture recognition (as the UIWebView probably sets a few recognizers itself, yours are skipped) :
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
Since the UIWebView does already handle a lot of touch events, it might be a bit tricky to add your own gesture recognizer.
I don't know if the user has to interact with your web view but you might try to add a UIView on top of the UIWebView and add the gesture recognizer to this view. It should still propagate unrecognized touch events to the underlying web view, thus leaving its interactivity intact.
Related
I have problem with swipe with three gestures
in my .m :
- (IBAction)click:(id)sender {
[_text setText:#"Hello World"];
}
- (IBAction)resetText:(id)sender {
[_text setText:#"Reset"];
}
when I clicked on the screen the output message "Hello World" will be shown on label
and it should shown "Reset" when I swipe three fingers from up to down, but it crashes
the weird thing is when I change the name of IBAction from "resetText" to for example "reset" or whatever name without capital letter it works. With any capital letter, it crashes
this is the Xcode file
I looked at your example project, and it seems that:
The crash only occurs on actual devices
The crash only occurs when the swipe gesture requires 3 or more touches
This looks to me like a bug in the UIGestureRecognizer class when added using Interface Builder, so there isn't much you can do about it now. I filed a radar (#14399827) with Apple describing this issue. You should probably do this as well.
However, you can work around this bug by creating the gesture recogniser in code instead of in the storyboard as you are now.
Remove the gesture recogniser from your storyboard (delete it completely), then add this to the viewDidLoad method in your view controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UISwipeGestureRecognizer *recogniser = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(resetText:)];
[recogniser setDirection:UISwipeGestureRecognizerDirectionDown];
[recogniser setNumberOfTouchesRequired:3];
[self.view addGestureRecognizer:recogniser];
}
I understand that this isn't ideal, as it may be more convenient in some cases to add the view controller directly to the storyboard, but unfortunately it seems that you can't currently do that due a bug in Apple's implementation.
I updated your view controller and now it is working for all gestures you want. Please check out and let me know.
- (void)viewDidLoad{
[super viewDidLoad];
//gesture for tap
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(click)];
[self.view addGestureRecognizer:tap];
//gesture for right swipe
UISwipeGestureRecognizer *rightRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(rightSwipeHandle:)];
rightRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
[rightRecognizer setNumberOfTouchesRequired:1];
[self.view addGestureRecognizer:rightRecognizer];
[rightRecognizer release];
//gesture for right swipe
UISwipeGestureRecognizer *leftRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(leftSwipeHandle:)];
leftRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
[leftRecognizer setNumberOfTouchesRequired:1];
[self.view addGestureRecognizer:leftRecognizer];
[leftRecognizer release];}
- (void)rightSwipeHandle:(UISwipeGestureRecognizer*)gestureRecognizer{ [_text setText:#" World"]; }
- (void)leftSwipeHandle:(UISwipeGestureRecognizer*)gestureRecognizer{ [_text setText:#"World"]; }
- (void)click{ [_text setText:#"Hello World"]; }
I have a UIView myView that responds to user taps.
I have added an animated view using:
[myView addSubView:animatedView];
But now the animated view, is blocking the taps, Since both use the same frame
How can I override that easily?
The best way I can see to you to do that is you declare the UITouchGesture in your views, and when you need to disable the touch in the view you can disable:
An example for a tap:
In your .h file you declare it:
UITapGestureRecognizer* tap;
And in your .m file
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[yourView addGestureRecognizer:tap];
And when you need to disable the tap you can simple:
[tap setEnable:NO];
or remove it:
[yourView removeGestureRecognizer:tap];
With that when you need to enable/disable will be easier and also will be easier to you to manage your touchs.
In my iPad Application I have multiple views on a screen.
What I want to do is apply a double tap Gesture Recognizer to the Navigation Bar. But I had no success, however when the same gesture recognizer applied to that view it works.
Here is the code I am using:
// Create gesture recognizer, notice the selector method
UITapGestureRecognizer *oneFingerTwoTaps =
[[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(oneFingerTwoTaps)] autorelease];
// Set required taps and number of touches
[oneFingerTwoTaps setNumberOfTapsRequired:2];
[oneFingerTwoTaps setNumberOfTouchesRequired:1];
[self.view addGestureRecognizer:oneFingerTwoTaps];
This works on view, but when this is done:
[self.navigationController.navigationBar addGestureRecognizer:oneFingerTwoTaps]
doesn't work.
For anyone else viewing this, here is a much simpler way to do this.
[self.navigationController.view addGestureRecognizer:oneFingerTwoTaps];
For this you need to subclass UINavigationBar, override the init button in it and add your gesture recognizer there.
So say you make a subclass called 'CustomNavigationBar' - in your m file you would have an init method like this:
- (id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super initWithCoder:aDecoder]))
{
UISwipeGestureRecognizer *swipeRight;
swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(didSwipeRight:)];
[swipeRight setDirection:UISwipeGestureRecognizerDirectionRight];
[swipeRight setNumberOfTouchesRequired:1];
[swipeRight setEnabled:YES];
[self addGestureRecognizer:swipeRight];
}
return self;
}
Then you need to set the class name of your navigation bar in interface builder to the name of your subclass.
Also it's handy to add a delegate protocol to your navigation bar to listen for methods sent at the end of your gestures. For example - in the case of the above swipe right:
#protocol CustomNavigationbarDelegate <NSObject>
- (void)customNavBarDidFinishSwipeRight;
#end
then in the m file - on the gesture recognised method (whatever you make it) you can trigger this delegate method.
Hope this helps
I am creating a custom UIView and adding a UITapGestureRecognizer on it. I have a handler for the tap gesture. But at the same time I want my UIView to listen to touchesBegan & touchesEnded methods. I have implemented gestureRecognizer:shouldReceiveTouch: method also but touchesBegan/touchesEnded methods does not get called. Any clue why?
Inside my custom UIView
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)iGestureRecognizer shouldReceiveTouch:(UITouch *)iTouch {
return YES;
}
Inside my view controller
MyCustomView aCustomView = [[[MyCustomView alloc] init] autorelease];
UIGestureRecognizer *myGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
[aCustomView addGestureRecognizer:myGestureRecognizer];
[myGestureRecognizer release];
You need to set cancelsTouchesInView (and likely delaysTouchesBegan and delaysTouchesEnded) to NO for the gesture recognizer. The default behavior of a gesture recognizer is to avoid having both it and the view process the touch. These settings let you fine-tune that behavior.
As stated earlier, you need to set the cancelTouchesInView property to NO on your UITapGestureRecognizer.
From the Apple Docs:
cancelsTouchesInView—If a gesture recognizer recognizes its gesture,
it unbinds the remaining touches of that gesture from their view (so
the window won’t deliver them). The window cancels the previously
delivered touches with a (touchesCancelled:withEvent:) message. If a
gesture recognizer doesn’t recognize its gesture, the view receives
all touches in the multi-touch sequence.
Further reading:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIGestureRecognizer_Class/
I want to call an action in two classes (a superview and a full screen subview) when the user single taps the screen. But, when I add a UITapGestureRecognizer to the subview, the one added to the superview is overridden. Is it possible to add a UITapGestureRecognizer to a subview without overriding the UITapGestureRecognizer added to the superview?
If so, how can I do this?
Thanks!
Edit:
From my main viewController "MyToolBerController", I'm adding the subview from another viewController as follows:
PhotoViewController *photoViewController = [[PhotoViewController alloc] initWithNibName:#"PhotoViewController" bundle:nil];
myPhotoView = photoViewController.view;
[self.view addSubview:myPhotoView];
I add the GestureRecognizer in the MyToolBerController like this:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTapFrom:)];
[singleTap setNumberOfTapsRequired:1];
singleTap.delegate = self;
[myPhotoView addGestureRecognizer:singleTap];
[singleTap release];
This all works fine, but I need to call a method in the PhotoViewController class when the view is tapped as well as in the MyToolBerController class.
When I add another UITapGestureRecognizer in the photoViewController, it overrides the UITapGestureRecognizer added in the superView.
Gesture recognizers can dispatch multiple actions when the gesture occurs. You can add the subview as another target of the gesture recognizer and only use a single UITapGestureRecognizer instance:
[tapRecognizer addTarget:theSubview action:#selector(whatever:)];
In your gesture recognizer selector method, pass the information along to the subview. There's no need to have multiple gesture recognizers for the same gesture. Something like:
- (IBAction)handleSingleDoubleTap:(UIGestureRecognizer *)sender
{
CGPoint tapPoint = [sender locationInView:sender.view.superview];
UIView *subview = [parentView viewWithTag:100];
[subview doSomethingWithPoint:tapPoint];
}
This of course means that your subview that needs to be notified should be given the tag 100 either in Interface Builder or in code when the view controller gets loaded.
Update based on Jonah's code:
So instead of retaining the view, retain the view controller:
PhotoViewController *photoViewController = [[PhotoViewController alloc] initWithNibName:#"PhotoViewController" bundle:nil];
self.myPhotoViewController = photoViewController;
Which means you need to declare it this way in the MyToolbarController header:
#property (nonatomic, retain) PhotoViewController *myPhotoViewController;
Then, when your gesture selector gets called, pass the message along to the view controller you retained. Something like:
- (IBAction)handleSingleTapFrom:(UIGestureRecognizer *)sender
{
CGPoint tapPoint = [sender locationInView:sender.view.superview];
[myPhotoViewController doSomethingWithPoint:tapPoint];
}
Of course the -doSomethingWithPoint: method is only for example. You can name and create any method you want that takes any parameter you want to pass in your PhotoViewController.
Let me know if you need further clarification.