【问题标题】:How do I Disable the swipe gesture of UIPageViewController?如何禁用 UIPageViewController 的滑动手势?
【发布时间】:2014-04-01 15:38:42
【问题描述】:

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

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

【问题讨论】:

  • 使用pageViewControllerObject.view.userInteractionEnabled = NO;
  • 不正确,因为它也阻止了它的所有内容。但我只需要阻止它的手势
  • 你为什么要这样做?你想从应用程序中获得什么行为?听起来您使架构过于复杂。你能解释一下你的目标是什么行为吗?
  • UIPageViewController 中的一个页面具有UINavigationController。客户希望在滑动手势上弹出导航控制器(如果可能的话),但页面视图控制器会处理滑动
  • @Srikanth 谢谢,这正是我所需要的!当我以编程方式触发页面更改并且有人通过滑动中断它时,我的页面视图崩溃了,因此每次在代码中触发页面更改时我都必须暂时禁用滑动...

标签: ios uiviewcontroller uigesturerecognizer uipageviewcontroller


【解决方案1】:

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

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

以上可以从Apple's documentation of UIPageViewController(概述,第二段)推导出来:

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

【讨论】:

  • 我也更喜欢这个解决方案。您可以将旧数据源保存在一个变量中,将数据源设置为 nil,然后恢复旧数据源以重新启用滚动。不是那么干净,但比遍历底层视图层次结构找到滚动视图要好得多。
  • 在响应文本字段子视图的键盘通知时禁用数据源会导致问题。遍历视图的子视图以查找 UIScrollView 似乎更可靠。
  • 这不是去掉屏幕底部的点吗?如果你没有滑动也没有点,那么使用 PageViewController 就没有多大意义了。我猜提问者想要保留这些点。
  • 这有助于解决我在页面异步加载并且用户做出滑动手势时所面临的问题。太棒了:)
  • A. RYounce 是正确的。如果您禁用 PageViewController 的数据源以响应键盘通知(即:您不希望用户在键盘打开时水平滚动),那么当您取消 PageViewController 的数据源时,您的键盘将立即消失。跨度>
【解决方案2】:
for (UIScrollView *view in self.pageViewController.view.subviews) {

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

        view.scrollEnabled = NO;
    }
}

【讨论】:

  • 这个保留了页面控制,不错。
  • 这是一个很好的方法,因为它保留了页面控制(小点)。手动更改页面时,您需要更新它们。我为此使用了这个答案stackoverflow.com/a/28356383/1071899
  • 为什么不for (UIView *view in self.pageViewController.view.subviews) {
  • 请注意,用户仍然可以通过点击左右半部分的页面控件在页面之间导航。
【解决方案3】:

我将user2159978的答案翻译成Swift 5.1

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

【讨论】:

    【解决方案4】:

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

    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
    

    【讨论】:

    • 为了更优雅,您可以添加:var scrollView: UIScrollView? { for view in view.subviews { if let subView = view as? UIScrollView { return subView } } return nil }
    • 这里的getter只返回最后一个子视图的启用状态。
    • @AndrewDuncan,这有什么问题?除非您有多个滚动视图,否则它可以解决问题...?
    • pagingEnabled 不是一个很酷的名字。 isSwipingEnabled更好
    • 可能不太酷,但遵循命名约定:)
    【解决方案5】:

    我已经为此奋斗了一段时间,并认为我应该在 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)
    }
    

    我希望这对某人有所帮助,它不像我想要的那样干净,但不会觉得太hacky等。

    编辑:如果有人想把它翻译成 Objective-C,请这样做:)

    【讨论】:

      【解决方案6】:

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

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

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

      【讨论】:

      • 你测试过你的代码吗? myPageViewController.gestureRecognizers 始终为空
      • 您的解决方案似乎适用于页面卷曲样式,但在我的应用中我使用滚动视图样式
      • 不适用于水平滚动。返回的值始终是一个空数组。
      • 你们想出这种滚动样式了吗?
      【解决方案7】:

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

      【讨论】:

        【解决方案8】:

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

        把它放在你的UIPageViewControllerviewDidLoad 函数中。 (斯威夫特)

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

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

        来自...

        Swipe to delete on a tableView that is inside a pageViewController

        【讨论】:

        • 谢谢卡特。我试过你的解决方案。然而,仍然存在 pageViewController 超越子表视图的水平滑动动作。如何确保当用户在子表格视图上滑动时,表格视图始终优先获得手势?
        • 这个问题的最佳答案!
        【解决方案9】:

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

        【讨论】:

          【解决方案10】:

          我是这样解决的(Swift 4.1)

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

          【讨论】:

            【解决方案11】:

            pageViewController.view.isUserInteractionEnabled = false

            这将禁用与页面的所有交互。如果您需要用户能够与内容进行交互 - 这不是您的解决方案。

            【讨论】:

              【解决方案12】:

              类似于@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
                      }
                  }
              

              【讨论】:

                【解决方案13】:

                翻译@user2159978 对 C# 的响应:

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

                【讨论】:

                  【解决方案14】:

                  感谢@user2159978 的回答。

                  我让它更容易理解。

                  - (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)您可以删除 pageViewController 的手势识别器:

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

                    如果你喜欢扩展:

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

                    pageViewController.removeGestureRecognizers

                    【讨论】:

                      【解决方案16】:

                      这样声明:

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

                      然后像这样使用它:

                      scrollView?.isScrollEnabled = true //false
                      

                      【讨论】:

                        【解决方案17】:

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

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

                        第 4 步:

                        到达新屏幕时更新滚动相关属性(如果您手动转换到某个屏幕,请不要忘记调用 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")
                            }
                        }
                        

                        【讨论】:

                          【解决方案18】:

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

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

                          它还会禁用导航。 pageControl(底部的点)适用于 iOS 11-13。在 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
                              }
                          }
                          

                          【讨论】:

                          • 这应该是正确答案!
                          【解决方案19】:

                          这是我的快速解决方案

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

                          【讨论】:

                            【解决方案20】:

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

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

                            【讨论】:

                              【解决方案21】:

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

                              所以我想出了这个:

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

                              把它放在viewDidLoad() 里面就可以了!

                              【讨论】:

                                【解决方案22】:
                                 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% 正常工作

                                【讨论】:

                                • 请评论您所面临的问题此代码完美运行:/
                                【解决方案23】:

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

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

                                【讨论】:

                                  【解决方案24】:

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

                                  不过,这是我方便/简短的 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
                                      }
                                  }
                                  

                                  【讨论】:

                                    猜你喜欢
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 2016-12-07
                                    相关资源
                                    最近更新 更多