UIGestureRecognizers for view in a scroll view - iphone

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

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.

UITapGestureRecognizer interefering with UIPinchGestureRecognizer

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.

touchesBegan method not called when scrolling in UIScrollView

I noticed touchesBegan method is not called in UIScrollView if i immediately place my finger on it and scroll. touchesBegan only gets called after i place my finger for a certain time duration before scrolling. Shouldn't touchesBegan always be called whenever there is a touch on the UIScrollView?
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(hideKeyBoard:)];
[scrollView addGestureRecognizer:gestureRecognizer];
-(void) hideKeyBoard:(id) sender
{
// Do whatever such as hiding the keyboard
}
I believe that UIScrollView intercepts these events, for the purpose of figuring out if you are going to be scrolling the containing view. Actually, it looks like it gets them first (which is opposite normal processing, where the deepest subview gets them first) so that it can figure out if there is a scroll or pinch gesture. See How does UIScrollView steal touches from its subviews?

Interrupting a scrolling/animating UIScrollView with a UIGestureRecognizer

I have a UIScrollView and a UITapGestureRecognizerwhich are both pretty basic, and have got them to work together in every way I need except one. The UIScrollView doesn't use any zooming, and the UITapGestureRecognizer just writes to the console for now.
I can get the UITapGestureRecognizer to write to the console when the UIScrollView is tapped, either while stationary or animating through setContentOffset, but I cannot get it to work when the UIScrollView is moving due to being swiped. When the UIScrollView is swiped and still in motion, the first tap after swiping will stop it moving, and then only the second tap is picked up by the UITapGestureRecognizer. I hope to get the first tap to both stop the UIScrollView from scrolling and also write to the console through the UITapGestureRecognizer.
I hope that my problem here is just through a gap in my knowledge of either the UIScrollView or UITapGestureRecognizer and there is just a Property to set to fix this, but so far no amount of reading has helped me with this issue. Any ideas on whether this is possible?
Edit: Thanks for the suggestions, please see below
Apologies, I don't think I explained myself very well. I realise the first movement on the stationary UIScrollView is a swipe (which in my case is just handled by the UIScrollView btw, not a gesture recognizer).
The problem is after swiping and releasing, if you tap while it is still in motion (and no other touch is in progress), it isn't picked up by the UITapGestureRecognizer, instead it is only picked up by the UIScrollView and stops the UIScrollView moving. If you let it decelerate then tap, this works fine. Also if it is moving due to an animation but not a swipe, the tap also works fine.
This is the same when using a `UILongPressGestureRecognizer' which is what I want to use ideally, but thought if I can't get it to work with a tap, I have no chance with that!
Sounds like you need to implement the
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
delegate method for your UITapGestureRecognizer and return YES for the UIGestureRecognizers you want to interpret tap gestures in sync with.
e.x.
// Somewhere in your code...
UIGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
tap.delegate = self;
[self.view addGestureRecognizer:tap];
[tap release];
// And the delegate method...
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
n.b. You may want to be more discriminating in deciding which UIGestureRecognizer(s) you wish to work in sync with. The example will work with every other gesture recognizer.
The problem is that when the user taps the first time to scroll the UIScrollView, this is not a tap, it is a swipe. It would be considered a tap only when the user taps and releases the finger almost at the same position (without dragging the finger through the screen).
What you could do to solve your problem is: use the UIScrollView delegate method scrollViewWillBeginDragging and then you would know when the user just tapped and will start dragging the scrollview, and also add the UITapGestureRecognizer for those real taps.

gesture recognizer for whole UIView

I want to find a way to animate a whole UIView which contains some ui components within it. Here's my code snippet:
UIRotationGestureRecognizer *rotationGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:#selector(rotatePiece:)];
[piece addGestureRecognizer:rotationGesture];
[rotationGesture release];
the "piece" is a uiview here which I want to rotate and it works perfectly fine. But when I add some other UI components (like UIbutton) inside it (piece view), gestures are not recognized properly when user touches on those UI components. Essentially, I want the parent to listen to the gestures even when user touches the child object.
Any ideas?
you just need to addGestureRecognizer to your subviews you add to your view. As in the rotate piece method you might be rotating your original view only,so it will work fine.
You can add a transparent UIView to cover the whole area and add the gesture recognizer there.