繁体   English   中英

如何禁用 UIPageViewController 的滑动手势?

[英]How do I Disable the swipe gesture of UIPageViewController?

在我的例子中,父UIViewController包含UIPageViewController ,其中包含UINavigationController ,其中包含UIViewController 我需要向最后一个视图 controller 添加滑动手势,但滑动操作就像它们属于页面视图 controller 一样。我尝试以编程方式和通过 xib 执行此操作,但没有结果。

因此,据我所知,在UIPageViewController处理其手势之前,我无法实现我的目标。 如何解决这个问题?

防止UIPageViewController滚动的记录方法是不分配dataSource属性。 如果您分配数据源,它将进入“基于手势”的导航模式,这正是您要阻止的。

如果没有数据源,当您想使用setViewControllers:direction:animated:completion方法时手动提供视图控制器,它将根据需要在视图控制器之间移动。

以上可以从苹果的 UIPageViewController 文档中推导出来(概述,第二段):

要支持基于手势的导航,您必须使用数据源对象提供视图控制器。

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

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

        view.scrollEnabled = NO;
    }
}

我将user2159978 的答案翻译成Swift 5.1

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

将@lee 的 (@user2159978) 解决方案作为扩展实现:

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
                }
            }
        }
    }
}

用法:(在UIPageViewController

self.isPagingEnabled = false

编辑:此答案仅适用于页面卷曲样式。 Jessedc 的答案要好得多:无论风格如何,都可以工作,并且依赖于记录的行为

UIPageViewController公开了它的一系列手势识别器,您可以使用它们来禁用它们:

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

我已经为此奋斗了一段时间,并认为我应该按照 Jessedc 的回答发布我的解决方案; 删除 PageViewController 的数据源。

我将此添加到我的 PgeViewController 类(链接到故事板中的页面视图控制器,继承UIPageViewControllerUIPageViewControllerDataSource ):

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
    }
}

然后可以在每个子视图出现时调用它(在这种情况下禁用它);

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

我希望这可以帮助某人解决问题,它不像我想要的那么干净,但不会感觉太笨拙等。

编辑:如果有人想将其翻译成 Objective-C,请执行 :)

UIPageViewController一个有用的扩展,用于启用和禁用滑动。

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
            }
        }
    }
}

如果您希望您的UIPageViewController保持它的滑动能力,同时允许您的内容控件使用它们的功能(滑动删除等),只需关闭canCancelContentTouches中的UIPageViewController

把它放在你的UIPageViewControllerviewDidLoad函数中。 (迅速)

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

UIPageViewController有一个自动生成的子视图来处理手势。 我们可以防止这些子视图取消内容手势。

从...

在 pageViewController 内的 tableView 上滑动以删除

@lee回答的快捷方式

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
    }
}

我是这样解决的(Swift 4.1)

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

pageViewController.view.isUserInteractionEnabled = false

这是我快速的解决方案

extension UIPageViewController {
    var isScrollEnabled: Bool {
        set {
            (self.view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView)?.isScrollEnabled = newValue
        }
        get {
            return (self.view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView)?.isScrollEnabled ?? true
        }
    }
}

类似于@user3568340 答案

斯威夫特 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
        }
    }

翻译@user2159978 对 C# 的回应:

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

感谢@ user2159978 的回答。

我让它更容易理解。

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

(Swift 4) 您可以删除 pageViewController 的gestureRecognizers:

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

如果您更喜欢扩展:

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

pageViewController.removeGestureRecognizers

像这样声明:

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

然后像这样使用它:

scrollView?.isScrollEnabled = true //false

我发现的答案对我来说看起来非常混乱或不完整,所以这里是一个完整且可配置的解决方案:

第1步:

让每个 PVC 元素负责判断是否启用了左右滚动。

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
        }
    }
}

每个 PVC 视图控制器都应该实现上述协议。

第2步:

在您的 PVC 控制器中,根据需要禁用滚动:

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

class SomeViewController: UIViewController {
    // ...    
}

第 3 步:

将有效的滚动锁定方法添加到您的 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
    }
}

第四步:

到达新屏幕时更新滚动相关属性(如果您手动过渡到某个屏幕,请不要忘记调用 enableScroll 方法):

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")
    }
}

有一种比这里的大多数答案所建议的更简单的方法,即在viewControllerBeforeviewControllerAfter数据源回调中返回nil

这将禁用 iOS 11+ 设备上的滚动手势,同时保留使用dataSource的可能性(例如用于页面指示器的presentationIndex / presentationCount

它还禁用导航通过。 iOS 11-13 的pageControl (底部的点)。 在 iOS 14 上,可以使用 UIAppearance 代理禁用底部点导航。

extension MyPageViewController: UIPageViewControllerDataSource {
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        return nil
    }
    
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        return nil
    }
}

如上所述,没有数据源是正确的答案。

不过,这是我的方便/简短的 Swift 5 解决方案。

它建立在我的 UIPageViewController 子类也包含它的数据源的事实之上。

extension MyPagerVC: UIPageViewControllerDataSource {
    var isPagingEnabled:Bool {
        get { dataSource == nil ? false : true }
        set { dataSource = newValue ? self : nil }
    }
}
class MyPagerVC:UIPageViewController {
    override func viewDidLoad() {
        isPagingEnabled = true
        // or simply set your dataSource
        dataSource = self
    }
}

更有效的返回方式,在 viewdidload (Swift 5) 上调用此方法:

private func removeSwipeGesture() {
    self.pageViewController?.view.subviews.forEach({ view in
        if let subView = view as? UIScrollView {
            subView.isScrollEnabled = false
            return
        }
    })
}

您可以实现UIPageViewControllerDataSource协议并为 previousViewController 和 nextViewController 方法返回nil 这将阻止UIPageViewController滑动到下一页或上一页。

    fileprivate func canSwipeToNextViewController() -> Bool {
        guard
            currentIndex < controllers.count,
            let controller = controllers[currentIndex] as? OnboardingBaseViewController,
            controller.canSwipeToNextScreen
        else {
            return false
        }

        return true
    }
}

// MARK: - UIPageViewControllerDataSource

extension ViewController: UIPageViewControllerDataSource {
    func presentationCount(for pageViewController: UIPageViewController) -> Int {
        controllers.count
    }

    func presentationIndex(for pageViewController: UIPageViewController) -> Int {
        currentIndex
    }

    func pageViewController(
        _ pageViewController: UIPageViewController,
        viewControllerBefore viewController: UIViewController
    ) -> UIViewController? {
        if let index = controllers.firstIndex(of: viewController) {
            if index > 0 {
                currentIndex -= 1
                return controllers[index - 1]
            } else {
                // Return nil to prevent swiping to the previous page
                return nil
            }
        }

        return nil
    }

    func pageViewController(
        _ pageViewController: UIPageViewController,
        viewControllerAfter viewController: UIViewController
    ) -> UIViewController? {
        if let index = controllers.firstIndex(of: viewController) {
            if index < controllers.count - 1,
               canSwipeToNextViewController() {
                currentIndex += 1
                return controllers[index + 1]
            } else {
                // Return nil to prevent swiping to the next page
                return nil
            }
        }

        return nil
    }
}

请记住将UIPageViewControllerdataSource属性设置为实现UIPageViewControllerDataSource协议的视图 controller。

我希望这有帮助。

枚举子视图以查找 UIPageViewController 的UIPageViewController对我不起作用,因为我在页面控制器子类中找不到任何 scrollView。 所以我想做的是禁用手势识别器,但要足够小心,不要禁用必要的识别器。

所以我想出了这个:

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

把它放在viewDidLoad() ,你就准备好了!

 override func viewDidLayoutSubviews() {
    for View in self.view.subviews{
        if View.isKind(of: UIScrollView.self){
            let ScrollV = View as! UIScrollView
            ScrollV.isScrollEnabled = false
        }

    }
}

将此添加到您的 pageviewcontroller 类中。 100% 工作

只需在您的UIPageViewController子类中添加此控件属性:

var isScrollEnabled = true {
    didSet {
        for case let scrollView as UIScrollView in view.subviews {
            scrollView.isScrollEnabled = isScrollEnabled
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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