Restrict double tap Zoom Out in UIWebview - ios

All,
In my project i am displaying a SVG image in UIWebview, I want to zoom, I am able to do that but when i tap two times unexpectedly, The complete view is Zoom Out, How can i do that? How can we restrict the zoom Out, Please help me out

If you want to disable all the touch events then disable UserInteractionEnabled property. For specific touch event, override existing tap events by adding UIGestureRecognizer over particular view, for your case - UIWebView instance.
- (void)viewDidLoad {
.....
.....
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTapOnWebView:)];
[doubleTap setNumberOfTapsRequired:2]; // 2 for double tap event
[doubleTap setDelegate:self]; // Add the <UIGestureRecognizerDelegate> protocol
[self.myWebView addGestureRecognizer:doubleTap];
}
- (void)handleDoubleTapOnWebView:(UITapGestureRecognizer *) gestureRecognizer {
//Do nothing.
}
Hope this helps.

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");
}
}

UIGestureRecognizers for single and double tap set in the xib

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.

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;
}

How to respond only to a single-tap gesture while letting objects behind respond to double-taps?

I'm displaying a document in a UIWebView. I want to place a hotspot over the document to trigger an action when it is tapped, but I also want to maintain the default UIWebView behavior of auto-zooming the document when it is double-tapped. I can't figure out how to respond to the single-taps while letting the UIWebView respond to the double-taps.
I first set up the hotspot as a transparent UIButton with an action, but double-tapping the hotspot resulted in the hotspot action being called twice. So I removed the action from the button and attached a single-tap gesture instead:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapAction:)];
singleTap.numberOfTapsRequired = 1;
singleTap.delegate = self;
[self.hotspot addGestureRecognizer:singleTap];
[singleTap release];
This works the same as the normal button action. But then I created a double-tap gesture, and configured it to block the single-tap gesture with requireGestureRecognizerToFail:
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(zoomWebView:)];
doubleTap.numberOfTapsRequired = 2;
doubleTap.delegate = self;
[self.hotspot addGestureRecognizer:doubleTap];
[doubleTap release];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapAction:)];
singleTap.numberOfTapsRequired = 1;
[singleTap requireGestureRecognizerToFail:doubleTap];
singleTap.delegate = self;
[self.hotspot addGestureRecognizer:singleTap];
[singleTap release];
- (void)zoomWebView:(UITapGestureRecognizer *)gesture {
NSLog(#"double tap");
}
With this setup, a single-tap on the hotspot calls singleTapAction and a double-tap on the hotspot calls zoomWebView (a custom method). This is good because singleTapAction is no longer called twice, but bad because the UIWebView no longer responds to the double-tap.
I tried forwarding the double-tap event from my doubleTap gesture to the UIWebView by subclassing UITapGestureRecognizer, overriding the touchesBegan and touchesEnded methods, and sending their arguments on to the corresponding methods of the UIWebView. When I did that, I could see that my subclass was receiving the events, but the UIWebView didn't respond to the forwarded events. This is to be expected because the Event Handling Guide for iOS says that we can only forward events to custom subclasses of UIView, not to UIKit framework objects.
Is there a way to prevent my single-tap gesture from responding to double-taps that doesn't divert the double-tap events? This seems like a basic requirement, but I can't see a straightforward way to do it. I read about and experimented with UIGestureRecognizer's touch delivery properties, but no combination of values stopped the single-tap gesture from consuming the double-tap gesture.
By the way, the relationship between the hotspot and the UIWebView in my view hierarchy is that of "cousins" -- they are subviews of two sibling views. If I add the gesture recognizers to hotspot view, the web view or their "grandparent" view, I get the same results.
Okay, I found a solution in two parts:
1) I had to add my gestures to a parent view of the UIWebView. Unmatched events don't travel through overlapping objects in a view from layer to layer as I was imagining. Instead, they travel through the hierarchy of views from child to parent. So as long as I was adding my double-tap gesture to a sibling or "cousin" view of the UIWebView, it was never going to proceed on to the web view. This means that I can't use buttons or views laid out in Interface Builder to determine multiple hotspot areas. Instead, I have to redirect all single-tap events to one method and then look at the touch positions to determine what action to trigger.
2) I had to add the gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: method to my view controller (the delegate of my gestures) and return YES. This allows my single-tap gesture to respond even when I'm displaying HTML, text or image content in the UIWebView, which implements its own gesture for these content types. I learned this part from this answer.
With these changes, I understand the hierarchy of events to be:
Single-tap web view: my single-tap gesture on the web view's parent view responds; the web view's single-tap gesture also responds if applicable because simultaneous gestures are enabled
Double-tap web view: my double-tap gesture on the web view's parent view responds (but doesn't do anything); my single-tap gesture does not respond because it is configured to only respond if the double-tap fails; the web view's double-tap event also responds because it is part of the view hierarchy (it seems that the double-tap functionality of the web view does not use a gesture because otherwise it would have take precedence over my own gesture in my original setup)
With that explanation out of the way, here's some working code:
- (void)viewDidLoad {
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doubleTapWebView:)];
doubleTap.numberOfTapsRequired = 2;
doubleTap.delegate = self;
[self.webViewParent addGestureRecognizer:doubleTap];
[doubleTap release];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapWebView:)];
singleTap.numberOfTapsRequired = 1;
[singleTap requireGestureRecognizerToFail:doubleTap];
singleTap.delegate = self;
[self.webViewParent addGestureRecognizer:singleTap];
[singleTap release];
}
- (void)doubleTapWebView:(UITapGestureRecognizer *)gesture {
NSLog(#"double-tap");
// nothing to do here
}
- (void)singleTapWebView:(UITapGestureRecognizer *)gesture {
NSLog(#"single-tap");
CGPoint touchLocation = [gesture locationInView:self.webViewParent];
float x = touchLocation.x;
float y = touchLocation.y;
CGRect frame = self.webViewParent.frame;
if (y < frame.size.height * .33) {
NSLog(#"top");
} else if (y > frame.size.height * .67) {
NSLog(#"bottom");
} else if (x < frame.size.width * .33) {
NSLog(#"left");
} else if (x > frame.size.width * .67) {
NSLog(#"right");
} else {
NSLog(#"center");
}
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
Always been a problem with mouse clicks since event handling began.
It's impossible at the instance of a single click, for any software to determine that a double click is about to happen.
So, if you want to handle single clicking and double clicking, you'll have to do your own double click handling.
Time the instances of single click, and generate a double click event of your own.
Hopefully that can transfer to tapping in your context.

How can I detect the touch event of an UIImageView?

I have placed an image (UIImageView) on the navigation bar. Now I want to detect the touch event and want to handle the event. How can I do that?
In practical terms, don't do that.
Instead add a button with Custom style (no button graphics unless you specify images) over the UIImageView. Then attach whatever methods you want called to that.
You can use that technique for many cases where you really want some area of the screen to act as a button instead of messing with the Touch stuff.
A UIImageView is derived from a UIView which is derived from UIResponder so it's ready to handle touch events. You'll want to provide the touchesBegan, touchesMoved, and touchesEnded methods and they'll get called if the user taps the image. If all you want is a tap event, it's easier to just use a custom button with the image set as the button image. But if you want finer-grain control over taps, moves, etc. this is the way to go.
You'll also want to look at a few more things:
Override canBecomeFirstResponder and return YES to indicate that the view can become the focus of touch events (the default is NO).
Set the userInteractionEnabled property to YES. The default for UIViews is YES, but for UIImageViews is NO so you have to explicitly turn it on.
If you want to respond to multi-touch events (i.e. pinch, zoom, etc) you'll want to set multipleTouchEnabled to YES.
To add a touch event to a UIImageView, use the following in your .m file:
UITapGestureRecognizer *newTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(myTapMethod)];
[myImageView setUserInteractionEnabled:YES];
[myImageView addGestureRecognizer:newTap];
-(void)myTapMethod{
// Treat image tap
}
You can also add a UIGestureRecognizer. It does not require you to add an additional element in your view hierarchy, but still provides you will all the nicely written code for handling touch events with a fairly simple interface:
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSwipe:)];
swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
[imgView_ addGestureRecognizer:swipeRight];
[swipeRight release];
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSwipe:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[imgView_ addGestureRecognizer:swipeLeft];
[swipeLeft release];
I've been on different threads on the past few hours trying to find a solution for my problem, to no avail. I see that many developers share this problem, and I think people here know about this. I have multiple images inside a UIScrollView, trying to get tap events on them.
I am not getting any events from an UIImangeView, but I do get an event from a similar UILable with very similar parameters I am setting to it. Under iOS 5.1.
I have already done the following:
set setUserInteractionEnabled to YES for both `UIImageView and parent
view .
set setMultipleTouchEnabled to YES for UIImageView.
Tried subclassing UIImageView, didn't help any.
Attaching some code below, in this code I initialize both a UIImageView and UILabel, the label works fine in terms of firing events. I tried keeping out irrelevant code.
UIImageView *single_view = [[UIImageView alloc]initWithFrame:CGRectMake(200, 200, 100, 100)];
single_view.image = img;
single_view.layer.zPosition = 4;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapGestureCaptured:)];
[single_view addGestureRecognizer:singleTap];
[single_view setMultipleTouchEnabled:YES];
[single_view setUserInteractionEnabled:YES];
[self.myScrollView addSubview:single_view];
self.myScrollView.userInteractionEnabled = YES;
UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
testLabel.backgroundColor = [UIColor redColor];
[self.myScrollView addSubview:testLabel];
[testLabel addGestureRecognizer:singleTap];
[testLabel setMultipleTouchEnabled:YES];
[testLabel setUserInteractionEnabled:YES];
testLabel.layer.zPosition = 4;
And the method which handles the event:
- (void)singleTapGestureCaptured:(UITapGestureRecognizer *)gesture
{
UIView *tappedView = [gesture.view hitTest:[gesture locationInView:gesture.view] withEvent:nil];
NSLog(#"Touch event on view: %#", [tappedView class]);
}
As said, the label tap is received.
Instead of making a touchable UIImageView then placing it on the navbar, you should just create a UIBarButtonItem, which you make out of a UIImageView.
First make the image view:
UIImageView *yourImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"nameOfYourImage.png"]];
Then make the barbutton item out of your image view:
UIBarButtonItem *yourBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:yourImageView];
Then add the bar button item to your navigation bar:
self.navigationItem.rightBarButtonItem = yourBarButtonItem;
Remember that this code goes into the view controller which is inside a navigation controller viewcontroller array. So basically, this "touchable image-looking bar button item" will only appear in the navigation bar when this view controller when it's being shown. When you push another view controller, this navigation bar button item will disappear.
You might want to override the touchesBegan:withEvent: method of the UIView (or subclass) that contains your UIImageView subview.
Within this method, test if any of the UITouch touches fall inside the bounds of the UIImageView instance (let's say it is called imageView).
That is, does the CGPoint element [touch locationInView] intersect with with the CGRect element [imageView bounds]? Look into the function CGRectContainsPoint to run this test.
First, you should place an UIButton and then either you can add a background image for this button, or you need to place an UIImageView over the button.
Or:
You can add the tap gesture to a UIImageView so that get the click action when tap on the UIImageView.
For those of you looking for a Swift 4 solution to this answer, you can use the following to detect a touch event on a UIImageView.
let gestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageViewTapped))
imageView.addGestureRecognizer(gestureRecognizer)
imageView.isUserInteractionEnabled = true
You will then need to define your selector as follows:
#objc func imageViewTapped() {
// Image has been tapped
}
Add gesture on that view. Add an image into that view, and then it would be detecting a gesture on the image too. You could try with the delegate method of the touch event. Then in that case it also might be detecting.

Resources