Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Continuous scrolling between UIPanGestureRecognizer and re-enabled UIScrollView

I've got a UIScrollView with paging enabled, and I've added my own UIPanGestureRegonizer to it. Under certain instances, my view controller will set scrollview.scrollEnabled = NO, and then add the pan gesture recognizer to it (I'm not using the scrollview's own recognizer).

So, scrolling is disabled but I'm waiting for user touches from my gesture recognizer. When it recognizes, it calls its action in which I re-enable scrolling.

The problem is, while the user still has a finger down, my scrollview doesn't track with the finger. It doesn't start scrolling until the finger is lifted and then dragged again. So my gesture recognizer is swallowing all the touches and not forwarding any to the scrollview.

I've tried toggling panGestureRecognizer.cancelsTouchesInView = NO; but it doesn't seem to have any effect (I'm currently removing this recognizer as soon as I re-enable scrolling but whether I do this or not doesn't solve my problem). I've also looked into the delays... properties of UIGestureRecognizer but they don't seem to be helping, either.

Any ideas? How can I get these events to continue to forward to my scrollview?

like image 495
jbrennan Avatar asked Jan 03 '12 20:01

jbrennan


1 Answers

The answer is a bit easier if you are only targeting iOS 5 and up, because in that case you really ought to reuse the UIScrollView panGestureRecognizer property.

In any case, the key step is to NOT reuse scrollEnabled, but instead to subclass UIScrollView, create your own property to manage this state, and override setContentOffset:.

    - (void) setContentOffset:(CGPoint)contentOffset
    {
        if(self.programaticScrollEnabled)
            [super setContentOffset:contentOffset];
    }

Here's one possible iOS 4+ Solution:

  1. Subclass UIScrollView (or subclass another subclass of UIScrollView, depending on your needs).
  2. Override all the initializers to ensure your setup code is called.
  3. Declare the BOOL property and override setContentOffset: as described above.
  4. In your setup code, set up a UIPanGestureRecognizer and set your state variable to allow programatic scrolling (assuming that's the default state you want):

    panRecognizer = [[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)] autorelease];
    //These properties may change according to your needs
    panRecognizer.cancelsTouchesInView = NO;
    panRecognizer.delaysTouchesBegan = NO;
    panRecognizer.delaysTouchesEnded = NO;
    [self addGestureRecognizer:panRecognizer];
    panRecognizer.delegate = self;
    
    self.programaticScrollEnabled = YES;
    
  5. Manage which gestures can occur simultaneously. In my case:

    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
    {
        return YES;
    }
    
  6. Turn programatic scrolling back on wherever you need it. For example:

    - (void)handleGesture:(UIPanGestureRecognizer *)gestureRecognizer
    {
        self.programaticScrollEnabled = YES;
    }
    
    - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
    {
        self.programaticScrollEnabled = YES;
        return YES;
    }
    
like image 82
Prometheus Avatar answered Nov 15 '22 03:11

Prometheus