How do I Disable the swipe gesture of UIPageViewController?

雨燕双飞 提交于 2019-11-26 05:55:32

问题


In my case parent UIViewController contains UIPageViewController which contains UINavigationController which contains UIViewController. I need to add a swipe gesture to the last view controller, but swipes are handled as if they belong to page view controller. I tried to do this both programmatically and via xib but with no result.

So as I understand I can\'t achieve my goal until UIPageViewController handles its gestures. How to solve this issue?


回答1:


The documented way to prevent the UIPageViewController from scrolling is to not assign the dataSource property. If you assign the data source it will move into 'gesture-based' navigation mode which is what you're trying to prevent.

Without a data source you manually provide view controllers when you want to with setViewControllers:direction:animated:completion method and it will move between view controllers on demand.

The above can be deduced from Apple's documentation of UIPageViewController (Overview, second paragraph):

To support gesture-based navigation, you must provide your view controllers using a data source object.




回答2:


for (UIScrollView *view in self.pageViewController.view.subviews) {

    if ([view isKindOfClass:[UIScrollView class]]) {

        view.scrollEnabled = NO;
    }
}



回答3:


I translate answer of user2159978 to Swift

func removeSwipeGesture(){
    for view in self.pageViewController!.view.subviews {
        if let subView = view as? UIScrollView {
            subView.scrollEnabled = false
        }
    }
}



回答4:


Implementing @lee's (@user2159978's) solution as an extension:

extension UIPageViewController {
    var isPagingEnabled: Bool {
        get {
            var isEnabled: Bool = true
            for view in view.subviews {
                if let subView = view as? UIScrollView {
                    isEnabled = subView.isScrollEnabled
                }
            }
            return isEnabled
        }
        set {
            for view in view.subviews {
                if let subView = view as? UIScrollView {
                    subView.isScrollEnabled = newValue
                }
            }
        }
    }
}

Usage: (in UIPageViewController)

self.isPagingEnabled = false



回答5:


I've been fighting this for a while now and thought I should post my solution, following on from Jessedc's answer; removing the PageViewController's datasource.

I added this to my PgeViewController class (linked to my page view controller in the storyboard, inherits both UIPageViewController and UIPageViewControllerDataSource):

static func enable(enable: Bool){
    let appDelegate  = UIApplication.sharedApplication().delegate as! AppDelegate
    let pageViewController = appDelegate.window!.rootViewController as! PgeViewController
    if (enable){
        pageViewController.dataSource = pageViewController
    }else{
        pageViewController.dataSource = nil
    }
}

This can then be called when each sub view appears (in this case to disable it);

override func viewDidAppear(animated: Bool) {
    PgeViewController.enable(false)
}

I hope this helps someone out, its not as clean as I would like it but doesn't feel too hacky etc.

EDIT: If someone wants to translate this into Objective-C please do :)




回答6:


Edit: this answer works for page curl style only. Jessedc's answer is far better: works regardless of the style and relies on documented behavior.

UIPageViewController exposes its array of gesture recognizers, which you could use to disable them:

// myPageViewController is your UIPageViewController instance
for (UIGestureRecognizer *recognizer in myPageViewController.gestureRecognizers) {
    recognizer.enabled = NO;
}



回答7:


If you want your UIPageViewController to maintain it's ability to swipe, while allowing your content controls to use their features (Swipe to delete, etc), just turn off canCancelContentTouches in the UIPageViewController.

Put this in your UIPageViewController's viewDidLoad func. (Swift)

if let myView = view?.subviews.first as? UIScrollView {
    myView.canCancelContentTouches = false
}

The UIPageViewController has an auto-generated subview that handles the gestures. We can prevent these subviews from cancelling content gestures.

From...

Swipe to delete on a tableView that is inside a pageViewController




回答8:


A useful extension of UIPageViewController to enable and disable swipe.

extension UIPageViewController {

    func enableSwipeGesture() {
        for view in self.view.subviews {
            if let subView = view as? UIScrollView {
                subView.isScrollEnabled = true
            }
        }
    }

    func disableSwipeGesture() {
        for view in self.view.subviews {
            if let subView = view as? UIScrollView {
                subView.isScrollEnabled = false
            }
        }
    }
}



回答9:


I solved it like this (Swift 4.1)

if let scrollView = self.view.subviews.filter({$0.isKind(of: UIScrollView.self)}).first as? UIScrollView {
             scrollView.isScrollEnabled = false
}



回答10:


Swifty way for @lee answer

extension UIPageViewController {
    var isPagingEnabled: Bool {
        get {
            return scrollView?.isScrollEnabled ?? false
        }
        set {
            scrollView?.isScrollEnabled = newValue
        }
    }

    var scrollView: UIScrollView? {
        return view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView
    }
}



回答11:


Similar to @user3568340 answer

Swift 4

private var _enabled = true
    public var enabled:Bool {
        set {
            if _enabled != newValue {
                _enabled = newValue
                if _enabled {
                    dataSource = self
                }
                else{
                    dataSource = nil
                }
            }
        }
        get {
            return _enabled
        }
    }



回答12:


The answers I found look very confusing or incomplete to me so here is a complete and configurable solution:

Step 1:

Give each of your PVC elements the responsibility to tell whether left and right scrolling are enabled or not.

protocol PageViewControllerElement: class {
    var isLeftScrollEnabled: Bool { get }
    var isRightScrollEnabled: Bool { get }
}
extension PageViewControllerElement {
    // scroll is enabled in both directions by default
    var isLeftScrollEnabled: Bool {
        get {
            return true
        }
    }

    var isRightScrollEnabled: Bool {
        get {
            return true
        }
    }
}

Each of your PVC view controllers should implement the above protocol.

Step 2:

In your PVC controllers, disable the scroll if needed:

extension SomeViewController: PageViewControllerElement {
    var isRightScrollEnabled: Bool {
        get {
            return false
        }
    }
}

class SomeViewController: UIViewController {
    // ...    
}

Step 3:

Add the effective scroll lock methods to your PVC:

class PVC: UIPageViewController, UIPageViewDelegate {
    private var isLeftScrollEnabled = true
    private var isRightScrollEnabled = true
    // ...

    override func viewDidLoad() {
        super.viewDidLoad()
        // ...
        self.delegate = self
        self.scrollView?.delegate = self
    }
} 

extension PVC: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        print("contentOffset = \(scrollView.contentOffset.x)")

        if !self.isLeftScrollEnabled {
            disableLeftScroll(scrollView)
        }
        if !self.isRightScrollEnabled {
            disableRightScroll(scrollView)
        }
    }

    private func disableLeftScroll(_ scrollView: UIScrollView) {
        let screenWidth = UIScreen.main.bounds.width
        if scrollView.contentOffset.x < screenWidth {
            scrollView.setContentOffset(CGPoint(x: screenWidth, y: 0), animated: false)
        }
    }

    private func disableRightScroll(_ scrollView: UIScrollView) {
        let screenWidth = UIScreen.main.bounds.width
        if scrollView.contentOffset.x > screenWidth {
            scrollView.setContentOffset(CGPoint(x: screenWidth, y: 0), animated: false)
        }
    }
}

extension UIPageViewController {
    var scrollView: UIScrollView? {
        return view.subviews.filter { $0 is UIScrollView }.first as? UIScrollView
    }
}

Step 4:

Update scroll related attributes when reaching a new screen (if you transition to some screen manually don't forget to call the enableScroll method):

func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
    let pageContentViewController = pageViewController.viewControllers![0]
    // ...

    self.enableScroll(for: pageContentViewController)
}

private func enableScroll(for viewController: UIViewController) {
    guard let viewController = viewController as? PageViewControllerElement else {
        self.isLeftScrollEnabled = true
        self.isRightScrollEnabled = true
        return
    }

    self.isLeftScrollEnabled = viewController.isLeftScrollEnabled
    self.isRightScrollEnabled = viewController.isRightScrollEnabled

    if !self.isLeftScrollEnabled {
        print("Left Scroll Disabled")
    }
    if !self.isRightScrollEnabled {
        print("Right Scroll Disabled")
    }
}



回答13:


Translating @user2159978's response to C#:

foreach (var view in pageViewController.View.Subviews){
   var subView = view as UIScrollView;
   if (subView != null){
     subView.ScrollEnabled = enabled;
   }
}



回答14:


Thanks to @user2159978's answer.

I make it a little more understandable.

- (void)disableScroll{
    for (UIView *view in self.pageViewController.view.subviews) {
        if ([view isKindOfClass:[UIScrollView class]]) {
            UIScrollView * aView = (UIScrollView *)view;
            aView.scrollEnabled = NO;
        }
    }
}



回答15:


(Swift 4) You can remove gestureRecognizers of your pageViewController:

pageViewController.view.gestureRecognizers?.forEach({ (gesture) in
            pageViewController.view.removeGestureRecognizer(gesture)
        })

If you prefer in extension:

extension UIViewController{
    func removeGestureRecognizers(){
        view.gestureRecognizers?.forEach({ (gesture) in
            view.removeGestureRecognizer(gesture)
        })
    }
}

and pageViewController.removeGestureRecognizers




回答16:


Declare it like this:

private var scrollView: UIScrollView? {
    return pageViewController.view.subviews.compactMap { $0 as? UIScrollView }.first
}

Then use it like this:

scrollView?.isScrollEnabled = true //false



回答17:


Enumerating the subviews to find the scrollView of a UIPageViewController didn't work for me, as I can't find any scrollView in my page controller subclass. So what I thought of doing is to disable the gesture recognizers, but careful enough to not disable the necessary ones.

So I came up with this:

if let panGesture = self.gestureRecognizers.filter({$0.isKind(of: UIPanGestureRecognizer.self)}).first           
    panGesture.isEnabled = false        
}

Put that inside the viewDidLoad() and you're all set!



来源:https://stackoverflow.com/questions/22098493/how-do-i-disable-the-swipe-gesture-of-uipageviewcontroller

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!