Gesture recognition with UIWebView - ios

I have set up some gesture recognition in an app that I'm building. One of the gestures is a single finger single tap, which hides the toolbar at the top of the screen. Works great except for one thing: a tap on a link causes the toolbar to go away, too.
Is it possible to detect a tap that was not a tap on a link? Could I do this by seeing where the tap occurred, and only take action if it didn't occur on an html link? Is this is possible, a pointer to how to determine if a link was tapped would be helpful.
Per Octys suggestion, I did attempt to wrap the UIWebView inside a UIView. I'm using interface builder, so I inserted a view in the hierarchy, and made the UIWebView a "child" of the new UIView. The hierarchy looks like this now:
I added an IBOutlet for the view in my view controller, and linked it up in interface builder.
I changed the gesture recognizer setup to look like this:
UITapGestureRecognizer* singleTap=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(handleSingleTap:)];
singleTap.numberOfTouchesRequired=1;
singleTap.delegate=self;
[self.UIWebViewContainer addGestureRecognizer:singleTap];
[singleTap release];
This code resides in viewDidLoad.
As before, the code correctly sees a single finger single tap, but a tap on a link in the UIWebView also causes the toolbar to go away. I only want the toolbar to go away if a link was NOT tapped.
Anyone have any suggestions?
Thank you.
Chris
Thanks
Chris

Try wrapping your UIWebView in a UIView container and set your gesture recognizers on the container view. Touch events that are not handled by the UIWebView will be passed up the view hierarchy and be intercepted by your container view, assuming it implements the appropriate handlers (and it is these handlers that should implement the code for hiding the toolbars...).

OK, so one "hack-ish" workaround is to make the view controller which houses the UIWebView a delegate of the gesture recognizer that you instantiate(UIGestureRecognizerDelegate) I am usually not a fan of solutions like this one, but...
Assign the gesture recognizer to the view that wraps you web view, and set it's delegate to self.
then in the delegate method gestureRecognizer:shouldRecieveTouch method, set the state to indicate the there was a tap event,
Return NO from the delegate method so the event propagates down the responder chain to the UIWebView.
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
_userTapped = YES;
[self performSelectorOnMainThread:#selector(hideMenuIfNotLink) afterDelay:??.?];
//??.?? amount of time to lapse to see if shouldStartLoadWithRequest gets called.
return NO;
}
-(void)hideMenuIfNotLink{
if(_userTapped)
//hideMenu
}
Now, in your UIWebViewDelegate shouldStartLoadWithRequest (which gets called when a user has in fact clicked a link)
if(_userTapped){
_userTapped = NO;
//possibly code to reshow or verify keep showing menu
}

You can use the UIWebViewDelegate protocol's -webView:​shouldStartLoadWithRequest:​navigationType: method.
If the navigationType is UIWebViewNavigationTypeLinkClicked, you can get the URL for the click by checking [request URL].

I've been looking for the same thing and found this: There is an iOS specific property that disables the callout when you hold your finger on a link. You add this into the styling (CSS) of your urls.
-webkit-touch-callout: none;

This worked for me.try adding the below line of code
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}

I was having the same problem. This solution worked for me:
1) make sure to add the protocol to your interface: UIGestureRecognizerDelegate
for example:
#interface ViewController : UIViewController _SlideViewProtocol, UIGestureRecognizerDelegate_
2) add this line of code
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
}
3)
UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(nextSlide)];
swipeGesture.numberOfTouchesRequired = 1;
swipeGesture.direction = (UISwipeGestureRecognizerDirectionLeft);
swipeGesture.cancelsTouchesInView = YES;
[swipeGesture setDelegate:self];
[self.view addGestureRecognizer:swipeGesture];

Related

Long press and pan gesture in UICollectionView

I'm working on a custom calendar downloaded from GitHub. It's a custom view with UICollectionView added in it to show date cells. I'm adding a functionality of dragging over the cells to get multiple date values. For that I've added UILongpressgesture
What I've tried,
#property (nonatomic, strong) UILongPressGestureRecognizer *dragDateGesture;
self.dragDateGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleDragBeginDate:)];
self.dragDateGesture.delegate = self;
self.dragDateGesture.minimumPressDuration = 0.05;
[self.collectionView addGestureRecognizer:self.dragDateGesture];
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)recognizer
{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
- (void)handleDragBeginDate:(UIPanGestureRecognizer *)recognizer
{
NSLog(#"Gesture recognised");
}
In the above code, I've added long press gesture and setting simultaneous gesture recognizer to yes. I'm not sure, whether adding a long press gesture will call the handleDragBeginDate method with UIPanGestureRecognizer getter. I'm new to gesture concept. It's not calling that method while dragging over collectionview.
What might be the issue here? can anyone please guide me on this?
If the way that I'm proceeding is wrong, new suggestions will be greatly appreciated.
As far as I know, the collection view doesn't have an edit mode similar to what the table view has. Fortunately, someone has already solved this problem for you.
UICollectionView has its own panGestureRecognizer and pinchGestureRecognizer. So it's no need to add. I suggest you get touch events in its delegate functions. This is one of these:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView;

Enabling touch event on Objects In a UIScrollview

I've a scrollview that has a UIView on it and on those Views, there's a UIImageView on it, three UILabel on it, I want to enable user Interaction on it, but wouldn't work. I've enabled setUserInteraction for both the UIView, UIScrollView, UILabels, UIImageView none is Responding to click actions at all. The layout look like the Image Below....
implement
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
so the gestures of the uiscroll view won't conflict with the subview's recognisers
basically what happens is that the UIScrollView won't pass the events to its subviews because the default of the above method is to return NO
This sounds too easy to be the problem, but I've made the following mistake: Everything is set just right, but I did not drag out one of the SentEvents like TouchUpInside to an action in the implementation.
check this way,
1> first check whether your view controller respond to UIGestureRecognizerDelegate this way
#interface RootViewController : UIViewController<UIGestureRecognizerDelegate>
2> then in design check you map scrollview property and delegate properly
3> apply this code in viewDidload
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapditected)];
tapGesture.delegate = self;
// prevents the scroll view from swallowing up the touch event of child buttons
tapGesture.cancelsTouchesInView = NO;
[self.scroller addGestureRecognizer:tapGesture];
4> check you apply this code
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
5> if all goes wll then this method should detected
-(void)tapditected{
}

Tap Gesture Recognizer for View but not Sub-View in CustomView

I have created a customViewController to act as a customActionsheet. In this customViewController I have a UIView as the main view (self.view) and an IBOutlet UIView that is the custom action sheet (actionSheetView). What I am trying to do is make this custom action sheet act like a regular actionsheet where if you tap in the dark area, in my case the view with blackColor background and alpha 5.0. This is what I have:
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(slideOut)];
tapGesture.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:tapGesture];
The problem with this is that the subview, the custom action sheet view, also gets the tapgesture so tapping anything on the actionSheetView will get the tap gesture. I have tried a few things like - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch and self.actionSheetView.userInteractionEnabled = NO; but both do not seem to work. Anyone have any idea of how I can make the self.view tappable but disable that gesture for self.actionSheetView? Any tips, guidance, or help is greatly appreciated. Thanks in advance.
I was working on this same style of system. I found it works if you:
Add a new UIView as a subview of the main UIView.
Create another UIView where you want the custom UI (could be a table like an action sheet, a picker, etc). Make sure this shows above the UIView in step 1.
Register the UITapGestureRecognizer on the UIView from step 1.
(Optional) Add extra buttons (e.g. "Cancel" and "Done") and set up a delegate that your custom controller fires.
The main UIView (self.view) should be opague, clear color with alpha of 1. The full subview (see step 1) can be any color (I used light gray color), but adjust the alpha value (0.5 for me).
Unfortunately I can't post an image because I don't have enough rep on the site yet.
If you're using IB and having trouble receiving taps, check under the Attributes Inspector (under the view section) to make sure that "User Interaction" is enabled.
You need to set the delegate
tapGesture.delegate = self.actionSheetView
//actionSheetView.m
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
return NO;
}

UITapGestureRecognizer on UIView and Its Subview Respond Together When Subview Is Tapped

UITapGestureRecognizer is applied to both UIImageView and its subview (UITextView). However, when I tap on subview, the receiver becomes subview and its parent view (i.e. UIImageView + UITextView). It should however be only subview because that was the one I tapped. I was assuming nested gestures would react first but apparently parent receives the fist tap and then it goes to child.
So, there are different solutions out there for various scenarios (not similar to mine but rather buttons inside scroll view conflict). How can I easily fix my issue without possible subclassing and for iOS 6+ support? I tried delaying touch on start for UIGestureRecognizer on UIImageView and I tried setting cancelsTouchesInView to NO - all with no luck.
Try the following code:
conform the <UIGestureRecognizerDelegate> to your class.
set yourGesture.delegate = self;
then add this delegate Method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// return YES (the default) to allow the gesture recognizer to examine the touch object, NO to prevent the gesture recognizer from seeing this touch object.
if([touch.view isKindOfClass: [UITextView class]] == YES)] {
return YES;
}
else {
return NO;
}
}
Hope it will solve your issue. Enjoy Coding..!!!!
That's exactly what is it supposed to do.
View hierarchy is like a tree structure and its traversal during a touch gesture starts from the root node. It is very likely for your parent view to receive gesture first and then its subviews. The traversal skips the nodes for which
userInteractionEnabled = NO.
since, you don't have any code I can't help you to play with this flag. A more general solution is to always set gesture only for your parentView and in the gesture delegates check the coordinates if it belongs to any one of the subview and if yes then call your gesture method for your subview.
Not a clean approach but works. !!
you should implement the UIGestureRecognizer delegate methods and apply the correct policy to the gesture, when multiple gesture are recognized

How do I ignore the tap if the user tapped on a UIBarButtonItem?

For some reason my UITapGestureRecognizer is blocking my toolbar buttons from being pressed when the recognizer is added to self.view and I don't want it to. In shouldReceiveTouch I want to return NO if the item is the toolbar button.
How do I do this, however? The items aren't UIBarButtonItems apparently, because when I put an if statement to check if touch.view is of that class, it ignores it. If I put a breakpoint there and inspect touch.view its class is UIToolbarTextButton. But [UIToolbarTextButton class] I get a "use of undeclared identifier UIToolbarTextButton" error.
Can I say if it's a subview of UIToolBar? What should I do?
Without any code, this is a hard question to answer... However it sounds like you are adding the Tapgesture recognizer to the same view as your toolbar... You could check the coordinates to see if they are within the CGrect of the uitoolbar... but really what I would recommend is creating a view which contains 2 subviews: one is your toolbar, the other is the main part of your view... then add the tapGesture only to the mainView. Good luck
The better solution is not to add the UITapGestureRecognizer to the self.view.
Add a new view with Interface Builder that covers all the area but the toolbar area.
Put all your controls inside it and add the tap gesture to it.
Alternative: Add this to your ViewDidLoad function or somewhere you find more appropriate.
for (UIView *v in [toolbar items])
{
v.tag = 5; // tag tool bar items
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (touch.view.tag == 5)
return NO;
return YES;
}

Resources