Pan gesture interferes with scroll

后端 未结 1 1737
执笔经年
执笔经年 2021-02-04 22:34

I have three view controllers that are part of a UIScrollView. I want to be able to swipe between the three, although one of the view controllers has a UIPanGestureRecognizer. I

1条回答
  •  北荒
    北荒 (楼主)
    2021-02-04 23:16

    Alright, here is the short answer:

    You have to use UIGestureRecognizer's method -requireGestureRecognizerToFail:.

    And here is the long answer:

    You have to make the pan gesture recognizer of your scroll view to succeed only if the pan gesture recognizer of TimerViewController fails. However that gesture (TimerViewController's gesture) should only succeed if the initial movement is vertical. If it is horizontal it should fail. To accomplish this we have to subclass UIPanGestureRecognizer and modify it to fit those needs. Here is what you have to do:

    1. Disregard ALL the changes you made from my previous answer
    2. Add VerticalPanGestureRecognizer to your project.
    3. Modify TimerViewController as shown.
    4. Modify ScrollViewController as shown.

    VerticalPanGestureRecognizer.h

    #import 
    #import 
    
    @interface VerticalPanGestureRecognizer : UIPanGestureRecognizer
    
    @end
    

    VerticalPanGestureRecognizer.m

    #import "VerticalPanGestureRecognizer.h"
    
    @interface VerticalPanGestureRecognizer ()
    
    @property (nonatomic, assign) CGPoint origLoc;
    
    @end
    
    @implementation VerticalPanGestureRecognizer
    
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    
        self.origLoc = [[touches anyObject] locationInView:self.view.superview];
        [super touchesBegan:touches withEvent:event];
    }
    
    -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    
        if (self.state == UIGestureRecognizerStatePossible) {
            CGPoint loc = [[touches anyObject] locationInView:self.view.superview];
            CGFloat deltaX = fabs(loc.x - self.origLoc.x);
            CGFloat deltaY = fabs(loc.y - self.origLoc.y);
            if (deltaY < deltaX)
                self.state = UIGestureRecognizerStateFailed;
        }
    
        [super touchesMoved:touches withEvent:event];
    }
    
    @end
    

    TimerViewController.h

    // Your imports here
    
    @interface TimerViewController : UIViewController
    
    {
        // Your ivars here
    }
    
    // Add the following property
    @property (nonatomic, strong) UIPanGestureRecognizer *pan;
    
    // Your methods here
    
    @end
    

    TimerViewController.m

    #import "TimerViewController.h"
    #import "VerticalPanGestureRecognizer.h"
    
    @implementation TimerViewController
    @synthesize pan = _pan;
    
    // prefersStatusBarHidden method here
    
    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil // Initialise view controller
    {
        self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    
        if (self) {
    
            // Instantiate the pan gesture as "VerticalPanGestureRecognizer"
            self.pan = [[VerticalPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]; // Create recogniser for a pan guesture
            self.pan.maximumNumberOfTouches = self.pan.minimumNumberOfTouches = 1;
            [self.view addGestureRecognizer:self.pan];
        }
    
        return self;
    }
    
    // The rest of your code here
    
    @end
    

    ScrollViewController.m

    - (void)viewDidLoad
    {
        // Your code here
    
        TimerViewController *tvc = [[TimerViewController alloc]init];
        CGRect frame = tvc.view.frame;
        frame.origin.x = 320;
        tvc.view.frame = frame;
    
        // Add the following line 
        [self.scrollView.panGestureRecognizer requireGestureRecognizerToFail:tvc.pan];
    
        [self addChildViewController:tvc];
        [self.scrollView addSubview:tvc.view];
        [tvc didMoveToParentViewController:self];
    
        // More code here
    }
    

    This new approach works perfectly. I tested it. Let me know if you have more questions.

    Cheers!

    UPDATE

    To answer the question you posted on the comments, here is what you have to do:

    - (void)viewDidLoad
    {
        [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
        BarsViewController *bvc = [[BarsViewController alloc]init];
        [self addChildViewController:bvc];
        [self.scrollView addSubview:bvc.view];
        [bvc didMoveToParentViewController:self];
    
        TimerViewController *tvc = [[TimerViewController alloc]init];
        CGRect frame = tvc.view.frame;
        frame.origin.x = 320;
        tvc.view.frame = frame;
    
        [self.scrollView.panGestureRecognizer requireGestureRecognizerToFail:tvc.pan];
        [self addChildViewController:tvc];
        [self.scrollView addSubview:tvc.view];
        [tvc didMoveToParentViewController:self];
    
        StopwatchViewController *svc = [[StopwatchViewController alloc] init];
        frame = svc.view.frame;
        frame.origin.x = 320*2;
        svc.view.frame = frame;
    
        [self addChildViewController:svc];
        [self.scrollView addSubview:svc.view];
        [svc didMoveToParentViewController:self];
    
        self.scrollView.contentSize = CGSizeMake(320*3, self.view.frame.size.height);
        self.scrollView.pagingEnabled = YES;
    
        [self.scrollView setShowsHorizontalScrollIndicator:NO];
    }
    

    Again, I tested it and it's working. You just have to add the gesture recognizer for the bars

    0 讨论(0)
提交回复
热议问题