Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Paging UIScrollView with two-finger pan and zoom

The test case is easily reproduced: download Apple's PhotoScroller sample app, and try to adapt it so that panning (both around a zoomed image, and between each image) only works with two fingers.

Setting the panGestureRecognizer for both the pagingScrollView and the imageScrollView to only accept a min & max of 2 touches seems like a good place to start, however it doesn't work. It lets you scroll around an image with two fingers just fine*, however paging then doesn't work.

I've tried so many combinations of settings and custom gesture recognizers, and I'm a bit stumped. Is a custom scroll view subclass going to be of any use, or can I somehow manipulate the scroll view delegate methods to make it work?

*EDIT: Actually, it doesn't scroll fine in this situation. The view no longer glides smoothly as with a single touch...

UPDATE: I'm still struggling with this one. I would appreciate some input from somebody who has played around with UIGestureRecognizers and UIScrollViews.

EDIT:

Setting the ImageScrollView class to only accept two touches:

- (id)initWithFrame:(CGRect)frame
{
    // ...
    // Template code
    // ...

    [self.panGestureRecognizer setMinimumNumberOfTouches:2];
    [self.panGestureRecognizer setMaximumNumberOfTouches:2];
}

Setting PhotoViewController's pagingScrollView to only accept two touches:

- (void)loadView
{
    // ...
    // Template code
    // ...

    [pagingScrollView.panGestureRecognizer setMinimumNumberOfTouches:2];
    [pagingScrollView.panGestureRecognizer setMaximumNumberOfTouches:2];
}

These modifications are made directly on top of the PhotoScroller sample app. I would expect these simple changes to work for two-finger interaction, however the side-effects are odd (as explained above).

like image 528
Stuart Avatar asked Nov 06 '11 03:11

Stuart


2 Answers

It would appear that this is either impossible, or very very difficult (i.e. roll your own scroll view from scratch). I used an Apple Tech support incident and they were unable to help. If a solution becomes available I'll happily mark it as the accepted answer!

like image 141
Stuart Avatar answered Nov 01 '22 05:11

Stuart


PROBLEM:

When the UIPanGestureRecognizer is underlying a UIScrollView (which unfortunately does also effect UIPageViewController) the maximumNumberOfTouches is not behaving as expected - the minimumNumberOfTouches however always limits the lower end correctly.

When monitoring these parameters they seem to do their job - it's just that UIScrollView doesn't honor them and ignores their values!


REMEDY:

You can find the solution in my answer to:

UIScrollView scrolling only with one finger


BTW:

The difference you experience between one finger and two finger panning is because with one finger you are using the panGestureRecognizer. With two fingers the pinchGestureRecognizer (which can also pan at the same time) kicks in and you have no deceleration phase and the view stops panning and zooming immediately after releasing your fingers. Deactivate the pinchGestureRecognizer and you will see that the panGestureRecognizer takes over - even for two fingers - and panning is smooth again... ;-)


SIMULTANEOUSLY - et voilà...

Delegate callbacks for a perfect simultaneous 2 finger scrolling and zooming behavior:

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    self.pinchGestureRecognizer.enabled = NO;
}

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
    self.pinchGestureRecognizer.enabled = NO;
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    self.pinchGestureRecognizer.enabled = YES;
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    self.pinchGestureRecognizer.enabled = YES;
}

- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {    
    self.panGestureRecognizer.enabled = NO;
}

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {    
    self.panGestureRecognizer.enabled = YES;
}

Fast pinch starts zooming!
Fast pan starts panning!

Stopping a decelerating pan with two new fingers down on the screen and start dragging again does not let the clumsy pinchGestureRecognizer take over (default) but rather smoothly go into the next pan/deceleration phase - like with one finger!


FOR PERFECTIONISTS:

Put 2 fingers on the screen and - DON'T MOVE NOW - If you don't start pinching within the first 0.5 seconds zooming gets locked and only panning is available:

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {

    if ([gestureRecognizer.view isMemberOfClass:[MY_CustomScrollView class]]) {

        NSLog(@"IN SCROLL-VIEW...");
        if (gestureRecognizer == self.pinchGestureRecognizer) {
            if (_pinchGestureLocked) {
                NSLog(@"...but TOO late for zooming...");
                return NO;
            } else {
                NSLog(@"...ZOOMING + PANNING...");
                return YES;
            }
        } else if (gestureRecognizer == self.panGestureRecognizer){
            if (gestureRecognizer.numberOfTouches > 2) {
                NSLog(@"...but TOO many touches for PANNING ONLY...");
                return NO;
            } else {
                NSLog(@"...PANNING...");
                return YES;
            }
        } else {

            NSLog(@"...NO PAN or PINCH...");
            return YES;
        }

    } else {
        NSLog(@"NOT IN SCROLL-VIEW...");
    }

    return YES;

}  

BOOL _pinchGestureLocked = NO;

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event];
    _pinchGestureLocked = YES;
}

- (void) touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [super touchesCancelled:touches withEvent:event];
    _pinchGestureLocked = NO;
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];
    _pinchGestureLocked = NO;
}

Happy gesture-recognizing!


like image 28
mramosch Avatar answered Nov 01 '22 06:11

mramosch