从第一页或 0 索引向后滑动时如何弹出 UIPageViewController?

How to pop UIPageViewController when swipe back from first page or 0 index?

我知道滑动默认由 UIPageViewController 管理,并且在此过程中调用了回调方法,如 willTransitionTodidFinishAnimating

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

我检查并发现 none 个回调方法为此操作调用。

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

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

的索引

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

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

我实现了如下的预期效果

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

var scrollView: UIScrollView?
var swipeBackPanGestureRecognizer: UIPanGestureRecognizer?

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

scrollView = view.subviews.filter{ [=11=] 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
}

基本上,您是在 scrollView 中的构建中添加一个新的 UIPanGestureRecognizer,它从内置的 interactivePopGestureRecognizer 中接管目标操作,负责向后滑动(我在这里找到了这个提示:)。

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 仅在我们自己的向后滑动手势识别器失败时才会被触发。

如果您的 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
        ...
    }
    ...
}