GangstaGraham
GangstaGraham

Reputation: 9385

Stop UIPanGestureRecognizer from moving UIView vertically when inside a UIScrollView

I would like to have a pan gesture that only moves the UIView horizontally.

So far, at the start of the pan gesture, only horizontal movements are allowed, but once the pan gesture has started, the UIView moves both horizontally and vertically.

i.e. it never moves vertically from its set position

Here is my current code:

- (void)panePanned:(UIPanGestureRecognizer *)gestureRecognizer
{    
    switch (gestureRecognizer.state) {
        case UIGestureRecognizerStateBegan: {
            self.paneStartLocation = [gestureRecognizer locationInView:self.mainView];
            self.paneVelocity = 0.0;
            break;
        }
        case UIGestureRecognizerStateChanged: {
            CGPoint panLocationInPaneView = [gestureRecognizer locationInView:self.mainView];
            CGFloat velocity = -(self.paneStartLocation.x - panLocationInPaneView.x);
            CGRect newFrame = self.mainView.frame;

            newFrame.origin.x += (panLocationInPaneView.x - self.paneStartLocation.x);
            if (newFrame.origin.x < 0.0) newFrame.origin.x = 0.0;
            self.mainView.frame = newFrame;

            if (velocity != 0) {
                self.paneVelocity = velocity;
            }
            break;
        }
        case UIGestureRecognizerStateEnded: {
            [self animate];
            break;
        }
        default:
            break;
    }
}

Thanks!

Upvotes: 0

Views: 1849

Answers (2)

danh
danh

Reputation: 62686

I did learn a couple things by building this myself. I think the pan logic can be simplified, and I think it can be made to work without disabling scrolling.

Try this: create a new single-view application project. Add a scroll view in storyboard. Add an outlet called 'scrollView' connecting to the ViewController. In ViewController.m add this code:

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;  // connected in storyboard
@end

@implementation ViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    for (int i=0; i<60; i++) {
        UIView *draggableView = [[UIView alloc] initWithFrame:CGRectMake(10, i*40, 34, 34)];
        draggableView.backgroundColor = [UIColor redColor];

        UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
        [draggableView addGestureRecognizer:pan];

        [self.scrollView addSubview:draggableView];
    }
    self.scrollView.contentSize = CGSizeMake(320, 60*40);
}

This adds a bunch of draggable views to the scroll view, giving each a pan gesture recognizer. I made a pan method like yours, except much simpler...

- (void)pan:(UIPanGestureRecognizer *)gr {

    switch (gr.state) {
        case UIGestureRecognizerStateBegan: {
            break;
        }
        case UIGestureRecognizerStateChanged: {
            UIView *view = gr.view;
            CGRect frame = view.frame;

            gr.view.frame = CGRectMake([gr translationInView:view].x, frame.origin.y, frame.size.width, frame.size.height);
            break;
        }
        case UIGestureRecognizerStateEnded: {
            break;
        }
        default:
            break;
    }
}

That's it. I don't disable scrolling, but we get the behavior that I think you're looking for. Glad your solution is working, but try out a project like this and see if it tells you anything about what you're up to.

Upvotes: 1

danh
danh

Reputation: 62686

Try disabling scrolling on state begun and re-enabling it on state ended.

[scrollView setScrollEnabled:NO];    // in case UIGestureRecognizerStateBegan
[scrollView setScrollEnabled:YES];   // in case UIGestureRecognizerStateEnded

Upvotes: 2

Related Questions