【问题标题】:How to pop UIPageViewController when swipe back from first page or 0 index?从第一页或 0 索引向后滑动时如何弹出 UIPageViewController?
【发布时间】:2020-08-14 18:47:07
【问题描述】:

我知道默认情况下滑动是由UIPageViewController 管理的,并且在此过程中会调用一些回调方法,例如willTransitionTodidFinishAnimating 等。

当我们从第一页或第零个索引向后滑动时,是否可以关闭 UIPageViewController

我检查并发现此操作没有调用任何回调方法。

【问题讨论】:

    标签: ios swipe uipageviewcontroller


    【解决方案1】:

    我能够达到如下预期的效果。

    1) 将以下属性添加到您的 UIPageViewController 子类:

    var scrollView: UIScrollView?
    var swipeBackPanGestureRecognizer: UIPanGestureRecognizer?
    

    2) 现在将以下代码添加到您的 UIPageViewController 子类的 viewDidLoad 方法中:

    scrollView = view.subviews.filter{ $0 is UIScrollView }.first as? UIScrollView
    
    if let scrollView = scrollView,
       let popGestureRecognizer = self.navigationController?.interactivePopGestureRecognizer,
       let targets = popGestureRecognizer.value(forKey: "targets") as? NSMutableArray {
        let panGestureRecognizer = UIPanGestureRecognizer()
        panGestureRecognizer.setValue(targets, forKey: "targets")
        panGestureRecognizer.delegate = self
        scrollView.addGestureRecognizer(panGestureRecognizer)
        swipeBackPanGestureRecognizer = panGestureRecognizer
    }
    

    基本上,您正在将新的 UIPanGestureRecognizer 添加到 scrollView 的构建中,它从内置的 interactivePopGestureRecognizer 中接管目标操作,负责向后滑动(我在这里找到了这个提示:https://stackoverflow.com/a/57487641/10060753)。

    3) 最后你需要实现这两个协议方法。确保在 UIPageViewController 子类的最后一个右括号下方添加以下代码,并将“YourPageViewController”替换为适当的子类名称:

    extension YourPageViewController: UIGestureRecognizerDelegate {
        func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
            guard let panRecognizer = gestureRecognizer as? UIPanGestureRecognizer,
                panRecognizer == swipeBackPanGestureRecognizer else {
                return true
            }
    
            guard let currentViewController = self.viewControllers?.first,
                pageViewController(self, viewControllerBefore: currentViewController) == nil else {
                return false
            }
    
            guard let gestureView = panRecognizer.view else {
                return true
            }
    
            let velocity = (panRecognizer.velocity(in: gestureView)).x
            return velocity > 0
        }
    
        func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
            if gestureRecognizer == swipeBackPanGestureRecognizer && otherGestureRecognizer == scrollView?.panGestureRecognizer {
                return true
            } else {
                return false
            }
        }
    }
    

    gestureRecognizerShouldBegin(_:) 的实现确保我们的向后滑动手势只有在以下情况下才会激活:a) 在第一页 (UIPageViewControllerDataSource viewControllerBefore == nil) 和 b) 从左向右滑动(速度 > 0).

    gestureRecognizer(_: shouldBeRequiredToFailBy:) 的实现确保了内置的 UIPageViewController panGestureRecognizer 只有在我们自己的回扫手势识别器失败时才会被触发。

    【讨论】:

      【解决方案2】:

      按照设计,UIPageViewController 不知道当前的视图控制器索引。每次调用 pageViewController(_:viewControllerAfter:) 时,您都可以创建一个 UIViewController 的新实例,并且您将拥有无限数量的页面。

      我怀疑您向 UIPageViewController 提供了某种数据源,无论是通过它的数据源还是通过setViewControllers(_:direction:animated:completion:)。因此,您有责任跟踪当前显示的UIViewController 的索引。

      您可以通过访问UIPageViewController 的委托中previousViewControllers 的第一个元素来检查正在显示的视图控制器:

      func pageViewController(_ pageViewController: UIPageViewController, 
                    didFinishAnimating finished: Bool, 
               previousViewControllers: [UIViewController], 
                   transitionCompleted completed: Bool)
      

      【讨论】:

      • 我已经在跟踪索引了,这里的问题是当我们在 UIPageViewController 的第一页并且我们向右滑动时,我们有没有机会得到那个事件?
      • 啊,好吧,我完全误解了你的问题。我认为一种解决方案可能是遍历UIPageViewController,因为它是内部UIScrollView,然后检查内容偏移量以查看用户滚动了多远。 stackoverflow.com/questions/22577929/…
      • 哦酷!我会试试的:)谢谢
      【解决方案3】:

      如果您的UIPageViewController 在容器视图中,您需要传递UINavigationController 或在容器的视图控制器内处理它:

      override func viewDidLoad() {
          super.viewDidLoad()
          self.delegate = self
          self.dataSource = self
          for view in view.subviews
          {
              if let scrollView = view as? UIScrollView
              {
                  scrollView.panGestureRecognizer.require(toFail: self.parentNavController!.interactivePopGestureRecognizer!);
              }
          }
          self.parentNavController!.interactivePopGestureRecognizer!.delegate = self
      }
      
      func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
          return self.parentNavController?.topViewController != self.parent || currentIndex == 0
      }
      

      在容器视图控制器中:

      override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
          switch (segue.identifier) {
              case "pageview_embed":
              let vc = segue.destination as! MyPageViewController
              vc.parentNavController = self.navigationController
              ...
              break
              ...
          }
          ...
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-18
        • 2015-10-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多