UITapGestureRecognizer interefering with UIPinchGestureRecognizer - iphone

I have a view controller that attaches a UITapGestureRecognizer to its main UIView, and enables the user to tap the screen to make the status, navigation, and tool bars reappear / disappear (like the photos app). I also have a UIScrollView attached to the main UIView which implements zooming and thus has its own UIPinchGestureRecognizer and UIPanGestureRecognizer to implement scrolling and zooming.
The problem I'm having, is when going to zoom / scroll the UIScrollView, it's very sensitive to picking up the UITapGestureRecognizer which is attached to the main UIView. It seems a lot of the time the UITapGestureRecognizer gets triggered when it shouldn't. Anyone have any ideas on how to fix this for versions of ios below 5.0? Is there someway I can override the simultaneous gestures delegate method for the UIGestureRecognizerDelegate in the UIScrollView and prevent the UITapGestureRecognizer from firing during other gestures?

Use the requireGestureRecognizerToFail: method.
[tapGestureRecognizer requireGestureRecognizerToFail:pinchGestureRecognizer];
This call tells the tap recognizer to wait for the pinch recognizer to fail.

Related

Zoomable UIScrollView with UIButtons capturing touches

I have a UIScrollView and I'm implementing the viewForZoomingInScrollView: delegate method which returns a UIImage which the user can zoom and pan. I've also got some UIButtons as sub views of the UIScrollView which I'm using as annotations, like Google Maps.
The problem I'm having is that a lot of the UIImage can be obscured by the UIButtons when zoomed right out. When trying to pinch to zoom the UIButtons are receiving the touch event instead and the zoom is not happening. You end up having to carefully place your fingers in clear space to zoom.
I note the Google Maps app seems to work ok when there are lots of annotation views, you can still pinch.
I guess I need to priorities the touches, the UIScrollView needs to respond to pinches and pans, while the buttons just taps.
Anyone have experience of this?
I had this exact issue, and it was kind of weird. It wasn't due to any gesture recognizer on the button, it was the UIScrollView's pinch gesture recognizer that was being forwarded to the UIButton for some reason. Also, the UIButton was responding to certain UIGestureRecognizerDelegate calls (like -gestureRecognizerShouldBegin:) but not others (like -gestureRecognizer:shouldReceiveTouch:). It's like the UIScrollView was selectively forwarding some delegate calls to the UIButton and some not.
I finally found a very easy way to solve this issue:
yourScrollView.pinchGestureRecognizer.delaysTouchesBegan = YES;
yourScrollView.pinchGestureRecognizer.delaysTouchesEnded = YES;
Luckily, the default pinch gesture recognizer on UIScrollView's are publicly accessible, and the two delaysTouches property are NO by default. When you set them to YES, if the gesture recognizer could possibly or does recognize a pinch gesture even when it starts on top of a UIButton, it won't forward those touches to the UIButton, and the UIButtons will no longer interfere with the UIScrollView's zooming.
try this
UIGestureRecognizer* tapRecognizer = nil;
for (UIGestureRecognizer* recognizer in yourButton) {
if ( ![recognizer isKindOfClass:[UITapGestureRecognizer class]] ) {
[yourButton removeGestureRecognizer:recognizer];
break;
}
}
so you remove all the gesture recognizers from the button different from UITapGestureRecognizer
take a look at UIView's hitTest:withEvent: method. Inside that method youll need to check for which view you want to return. The view you return will be the one recieving the touches. for example, you can subclass the button and override that method to return the ImageView for your particular scenario.
I fixed this problem in a different way, but this might not be your case.
My buttons were added on an image view, the image view being added by itself to another container view. In the:
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
... method, I returned the image view - bad. Changing to return my very first container view in hierarchy fixed the touches.

UIGestureRecognizer not receiving touches during UIScrollView deceleration

I've been trying to create a UITextView subclass that handles swiping away the keyboard like in the Message.app.
I have a UIPanGestureRecognizer added to the keyWindow of my app, and the gesture delegate is configured for shouldRecognizeSimultaneouslyWithGestureRecognizer. Everything works fine except for when the UIScrollView is decelerating, during that phase it is possible to pan without the touches being registered.
You can take a look at a very simple github sample project here.
I have tried adding the UIPanGestureRecognizer directly to the viewController.view and to the scrollView, same issue occurs. I have also tried setting scrollView.panGestureRecognizer requireGestureRecognizerToFail: with my UITextView subclass gesture recognizer.
Any ideas as to why this may be happening?
Instead of creating a new UIPanGestureRecognizer, maybe you could use the one on the UIScrollView, and add your own pan logic to that gesture recognizer with - (void)addTarget:(id)target action:(SEL)action.

UIGestureRecognizers for view in a scroll view

I'm using a few UIGestureRecognizers to pan, rotate and scale a view, which resides inside a scroll view.
My problem is that sometimes the scroll view eats the touches before the gesture recognizers do, so when this happens I end up zooming the scroll view instead of dragging the view. (It doesn't happen all the time. I still can't describe how to reproduce this behavior).
I'm pretty sure this can be solved in some way. For example MPMoviePlayerController doesn't have this problem: in fact, you can put it in a scroll view, and when you pinch it, it works just fine (i.e. it doesn't zoom the outer scroll view too). Does anyone know how does MPMoviePlayerController achieve this?
I've already searched for answers on SO, with no results. Thanks!
Disclaimer: This is just an idea, I haven't tested this.
UIGestureRecognizerDelegate defines gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:. You could try implementing this on your gesture recognizer delegate. This won't prevent the scroll view from zooming. To prevent zooming you could try temporary setting maximumZoomScale minimumZoomScale to zoomScale in the recognizer action method.
At a guess your rotation is conflicting with zoom - both probably require two touches?
In which case try creating a one touch rotation.
Or disable zooming on the scrollview and implement zooming/scaling via pinch gesture recognizer.
see:
http://www.icodeblog.com/2010/10/14/working-with-uigesturerecognizers/
Could you not set scrollEnabled(NO) on your UIScrollView?
http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIScrollView_Class/Reference/UIScrollView.html
If the value of this property is YES , scrolling is enabled, and if it
is NO , scrolling is disabled. The default is YES.
When scrolling is disabled, the scroll view does not accept touch
events; it forwards them up the responder chain.
Things like zoomToRect:animated: don't say anything about being disabled by this flag; I'd assume that would keep working when you pan/zoom/scroll in response to your gesture events.
I think you can differentiate zoom in/zoom out and scrolling functionality by identify number of tap option on UIScrolView. Here i do same things as you want. you may try it.
- (void)viewDidLoad {
[super viewDidLoad];
imgview=[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"2.png"]];
view1=[[UIView alloc]initWithFrame:CGRectMake(0, 0, 300, 400)];
view1.backgroundColor=[UIColor greenColor];
[view1 addSubview:imgview];
objscrollview.contentMode=UIViewContentModeScaleToFill;
objscrollview.contentSize=CGSizeMake(300, 400);
objscrollview.minimumZoomScale=1;
objscrollview.maximumZoomScale=10;
[objscrollview addSubview:view1];
UITapGestureRecognizer *singleFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap)];
singleFingerTap.numberOfTapsRequired = 1;
[objscrollview addGestureRecognizer:singleFingerTap];
[singleFingerTap release];
}
//Single tap on scrollview call below method
-(void)handleSingleTap
{
NSLog(#"Singletap identify");
}
//While perform zoom in/zoom out action on scroll view it's delegate method call
//In this method, we are return view that want to zoom in/Zoom out..
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView1
{
NSLog(#"hi++++++++++++++++++");
return imgview;
}

How to pass a 'tap' to UIButton that is underneath UIView with UISwipeGestureRecognizer?

I have a UIButton underneath a (transparent) UIView. The UIView above has a UISwipeGestureRecognizer added to it, and that is its only purpose - to detect certain swipe gestures. I want all other touches to be ignored by that UIView, and passed to other views (such as my UIButton underneath). Currently, the UIView above seems to be detecting the tap (for example), doing nothing (as it should be), and not letting the UIButton underneath get a chance to respond.
I would prefer not to implement my own swipe recognizer, if possible. Any solutions / advice? I basically just want to know how to tell a UIView to pay attention to only a certain type of added gesture recognizer, and ignore (and thus let through to views behind) all other touches.
Have you set:
mySwipeGesture.cancelsTouchesInView = NO;
to allow the touches to be sent to the view hierarchy as well as the gesture?
Additionally, ensure that the view on top is:
theTransparentView.opaque = NO;
theTransparentView.userInteractionEnabled = YES;
I've had pretty good success attaching gestures to the parent view without needing to create a transparent subview on top for the gesture. Are you sure you need to do that?
I must have just been in a funk yesterday - I woke up with a simple solution today. Add the UISwipeGesture to a view which is a superview to both the UIView and the UIButton. Then, when processing those swipes, figure out where the swipe originated, and whether that point is in the frame of where I used to have the UIView. (As I mentioned, the only reason for the existence of the UIView was to define a target area for these swipe gestures.)
Can't you put your button on top of the view and add gesture recognisers to that button too?
In the end, your UIButton inherits form UIView via UIControl. Therefore there is practically nothing that you could do with a view but not with a button.
In my case, I fixed it by not using a button, but rather a UITapGestureRecognizer. My pan gesture recognizer was added to the background view, and the tap gesture was added to a view above it.

Why does touchesBegan stop working when UIImageView in placed inside a UIScrollView?

UIView -> UIImageView
I know I have things somewhat working ok since I can tap on my UIImageView and see an NSLog() statement in my touchesBegan method.
.
UIView -> UIScrollView -> UIImageView
I drag that same UIImageView into a UIScrollView and touchesBegan no longer gets called when I tap on my UIImageView. (I haven't changed anything else. All the same connections, methods, and code remains unchanged.)
Why does touchesBegan no longer work? And what can I do to get it working again?
Add uitapgesture to get event
Code is
UITapGestureRecognizer *ges11=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(Handeltap:)];
[imagename addGestureRecognizer:ges11];
Create one action name "Handeltap" U will get called there.
by default UIImageView don't handle user gestures.
set UIImageView instance's userInteractionEnabled to YES
Have a look at the documentation for UIScrollView.
Because a scroll view has no scroll bars, it must know whether a touch signals an intent to scroll versus an intent to track a subview in the content. To make this determination, it temporarily intercepts a touch-down event by starting a timer and, before the timer fires, seeing if the touching finger makes any movement. If the timer fires without a significant change in position, the scroll view sends tracking events to the touched subview of the content view. If the user then drags their finger far enough before the timer elapses, the scroll view cancels any tracking in the subview and performs the scrolling itself. Subclasses can override the touchesShouldBegin:withEvent:inContentView:, pagingEnabled, and touchesShouldCancelInContentView: methods (which are called by the scroll view) to affect how the scroll view handles scrolling gestures.
I'd also recommend reading the Scroll View Programming Guide.