Horizontal scrolling UIScrollView with vertical pan gesture
Asked Answered
W

3

9

I am attempting to implement a UIScrollView where horizontally panning scrolls through pictures in the scrollview but vertically panning performs another action I have. Right now I have a UIScrollView that is paginated with vertical scrolling disabled that works just fine for scrolling through pictures, but am going crazy trying to find a way to intercept vertical pans and call my own method instead of the vertical pans just being eaten up by the scrollview. I was able to do

    UIPanGestureRecognizer *panUp = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
[self.scrollView addGestureRecognizer:panUp];
[scrollView.panGestureRecognizer setEnabled:NO];

Which gives me total control over the panning in my handlePanGesture: method, but this isn't particularly helpful because then I feel like I am essentially going to be rewriting all the scrolling logic which I am trying to avoid. Any help or guidance on how to achieve this would be greatly appreciated.

Thanks!

Wickner answered 2/10, 2012 at 16:41 Comment(4)
What is it you're trying to do with your vertical pan? Does it involve scrolling or not?Nork
It doesn't involve scrolling. I have a subview that I want to animate into the same parent view of the scrollview (the scrollview only takes up a portion of its parent's view)Wickner
Then I don't understand your statement: "I feel like I am essentially going to be rewriting all the scrolling logic". Does you horizontal pan work correctly? Is the panUp still being intercepted by the scrollView?Nork
I know I'm late but setting the shouldRecognizeSimultaneouslyWithGestureRecognizer to return YES will work for this but you MUST also set your pan gesture .delegate to self. I left out setting the delegate and it wasn't working for me. I see you left out the delegate in your code above too. Hopefully this helps future users.Stepper
N
4

I think this will be difficult if you want to use a pan gesture because of the scroll view's use of those gestures. However, if you could use a swipe, it is easy to implement.

    UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
    swipe.direction = UISwipeGestureRecognizerDirectionDown | UISwipeGestureRecognizerDirectionUp;
    [self.scrollView addGestureRecognizer:swipe];

I tested this, and it worked fine to call the handler, and you don't need to disable the scroll view's pan gesture recognizer (in my test, the scroll view was tall enough and the content short enough that there was no vertical scrolling -- I don't know if this is necessary or not).

Nork answered 2/10, 2012 at 23:59 Comment(2)
Hey, thanks a lot for your help. This worked perfectly and saved me from driving myself even more crazy trying to get the pan to work. I was avoiding using the swipe because I thought it wouldn't respond to slow drags up on the screen, but it actually worked well even with the slow swipes. Thanks again.Wickner
Useless. Did you try swipe gesture by yourself? It is not enough customizable to work with it.Challenge
A
10

As of iOS 5, UIScrollView exposes its UIPanGestureRecognizer. Set your custom swipe’s delegate to self and make the gestureRecognizer a property or ivar, and make your class conform to the <UIGestureRecognizerDelegate> protocol. Then, implement UIGestureRecognizerDelegate’s – gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: like so:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    if ([gestureRecognizer isEqual:self.swipe] && [otherGestureRecognizer isEqual:self.scrollView.panGestureRecognizer])
    {
         return NO;
    }
    return YES; // the default for this method
}
Applause answered 3/10, 2012 at 0:23 Comment(3)
I like this answer cause it puts more control in my hands with pan gesture rather than just simple swipe. I had an issue with my uitableview working simultaneously with side-to-side pan gesture. This solved it.Studhorse
Doesn't work! otherGestureRecognizer is UIScrollViewDelayedTouchesBeganGestureRecognizer and of course not equal to self.scrollView.panGestureRecognizerChallenge
That method will be called for every gesture recognizer that might need to recognize simultaneously with any gesture recognizer for which your object is the delegate.Applause
R
10

I added a UIPanGestureRecognizer then setupgestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: like this:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    if ([gestureRecognizer isEqual:self.panGesture] && [otherGestureRecognizer isEqual:self.collectionView.panGestureRecognizer]){
        return YES;
    }
    return NO;
}

I also added a check in gestureRecognizerShouldBegin:

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if ([gestureRecognizer isEqual:self.panGesture]) {
        if (gestureRecognizer.numberOfTouches > 0) {
            CGPoint point = [gestureRecognizer locationOfTouch:0 inView:gestureRecognizer.view];
            CGFloat distX = abs(self.collectionView.lastTouchPos.x - point.x);
            CGFloat distY = abs(self.collectionView.lastTouchPos.y - point.y);
            if (distX > distY) {
                return NO;
            }
        } else {
            return NO;
        }
    }
    return YES;
}

to only use self.panGesture when panning up or down.

Update

https://mcmap.net/q/150641/-uipangesturerecognizer-only-vertical-or-horizontal appears to have a better method for gestureRecognizerShouldBegin

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if ([gestureRecognizer isEqual:self.panGesture]) {
        if (gestureRecognizer.numberOfTouches > 0) {
            CGPoint translation = [self.panGesture velocityInView:self.collectionView];
            return fabs(translation.y) > fabs(translation.x);
        } else {
            return NO;
        }
    }
    return YES;
}
Royceroyd answered 13/11, 2013 at 5:21 Comment(1)
Worked for me, except I needed fabs(translation.y) < fabs(translation.x)Recognition
N
4

I think this will be difficult if you want to use a pan gesture because of the scroll view's use of those gestures. However, if you could use a swipe, it is easy to implement.

    UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
    swipe.direction = UISwipeGestureRecognizerDirectionDown | UISwipeGestureRecognizerDirectionUp;
    [self.scrollView addGestureRecognizer:swipe];

I tested this, and it worked fine to call the handler, and you don't need to disable the scroll view's pan gesture recognizer (in my test, the scroll view was tall enough and the content short enough that there was no vertical scrolling -- I don't know if this is necessary or not).

Nork answered 2/10, 2012 at 23:59 Comment(2)
Hey, thanks a lot for your help. This worked perfectly and saved me from driving myself even more crazy trying to get the pan to work. I was avoiding using the swipe because I thought it wouldn't respond to slow drags up on the screen, but it actually worked well even with the slow swipes. Thanks again.Wickner
Useless. Did you try swipe gesture by yourself? It is not enough customizable to work with it.Challenge

© 2022 - 2024 — McMap. All rights reserved.