简体   繁体   中英

Combine UIPageViewController swipes with iOS 7 UINavigationController back-swipe gesture

I have a navigation controller that pushes a view-controller ( PARENT ) that contains an UIPageViewController ( PAGES ). Now I used pan/swipe gestures to switch between the children of the page-view-controller. However, I can no longer pop the PARENT -view controller using the swipe gesture from the left border of the screen, because it is interpreted as a gesture in PAGES .

Is it possible to accomplish that swipe-to-pop when the left-most view-controller is shown?

Two ideas:

  1. Return nil in pageViewController:viewControllerBeforeViewController -> doesn't work.

  2. Restrict the touch area, as described here .

Or is there a more straightforward way?

I had the same situation as @smallwisdom, but handled it differently.

I have a view controller A that I push on top of my navigation controller's stack. This view controller A contains a horizontal scroll view that stretches all the way from left side of the screen to the right.

In this scenario, when I wanted to swipe the screen to pop view controller A from navigation controller's stack, all I ended up doing was scrolling this horizontal scroll view.

The solution is pretty simple.

Inside my view controller A , I have code like this:

_contentScrollView = [[UIScrollView alloc] init];
[self.view addSubview:_contentScrollView];
for (UIGestureRecognizer *gestureRecognizer in _contentScrollView.gestureRecognizers) {
   [gestureRecognizer requireGestureRecognizerToFail:self.navigationController.interactivePopGestureRecognizer];
}

It works great. What this does? It is telling the scrollView's gesture recognizers that they have to wait to see if some other gesture recognizer will recognize the current gesture.

If that other fails to recognize, then they will no longer have to wait and they can try to recognize the current gesture.

If that other recognizer succeeds and recognizes the current gesture, then all of the gesture recognizers that have been waiting will automatically fail.

This other gesture recognizer they have to wait is set to be the navigation controller's interactivePopGestureRecognizer. He is in charge for the swipe-to-go-back gestures.

I mostly agree with @ancajic's answer. I would like to provide an extra-case when you set UIPageViewController's transitionStyle to 'Scroll', in which you'll not get gestureRecognizers to be set, the workaround is:

if (self.navigationController?.interactivePopGestureRecognizer != nil)
{
    for view in self.pageViewController!.view.subviews
    {
        if let scrollView = view as? UIScrollView
        {
            scrollView.panGestureRecognizer.requireGestureRecognizerToFail(self.navigationController!.interactivePopGestureRecognizer!);
        }
    }
}

I had a similar issue in one of my projects and used the following method. In my case, it was one of those left-side menus that were really popular before iOS 7.

My solution was to set the UINavigationControllerDelegate and then implemented the following:

- (void)navigationController:(UINavigationController *)navigationController
       didShowViewController:(UIViewController *)viewController
                    animated:(BOOL)animated {

    // enable the interactive menu gesture only if at root, otherwise enable the pop gesture
    BOOL isRoot = (navigationController.viewControllers.firstObject == viewController);
    self.panGestureRecognizer.enabled = isRoot;
    navigationController.interactivePopGestureRecognizer.enabled = !self.panGestureRecognizer.enabled;
}

EDIT:

Additionally, you need a hook into the UIPageViewController's gesture recognizers. (They aren't returned by the gestureRecognizers property for a scroll view style page view controller.) It's annoying, but the only way I've found to access this is to iterate through the scrollview's gesture recognizers and look for the pan gesture. Then set a pointer to it and enable/disable based on whether or not you are currently displaying the left-most view controller.

If you want to keep the right swipe enabled, then replace the pan gesture with a subclassed pan gesture recognizer of your own that can conditionally recognize based on the direction of the pan gesture.

First, find UIPageViewController's scrollView

extension UIPageViewController {
    var bk_scrollView: UIScrollView? {
        if let v = view as? UIScrollView {
            return v
        }
        // view.subviews 只有一个元素,其类型是 _UIQueuingScrollView,是 UIScrollView 的子类
        // view.subviews have only one item of which the type is _UIQueuingScrollView, which is kind of UIScrollView's subclass.
        for v in view.subviews where v is UIScrollView {
            return v as? UIScrollView 
        }
        return nil
    }
}

Second, set gestureRecognizer 's dependence.

override func viewDidLoad() {
    super.viewDidLoad()
    if let ges = navigationController?.interactivePopGestureRecognizer {
        pageViewController.bk_scrollView?.panGestureRecognizer.require(toFail: ges)
    }
}

Swift version of @Lcsky's answer:

if let interactivePopGesture = self.navigationController?.interactivePopGestureRecognizer, let pageViewController = self.swipeVC?.pageViewController {
    let subView = pageViewController.view.subviews
    for view in subView  {
        if let scrollView = view as? UIScrollView{
            scrollView.panGestureRecognizer.require(toFail: interactivePopGesture)
        }
    }    
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM