【发布时间】:2020-08-14 18:47:07
【问题描述】:
我知道默认情况下滑动是由UIPageViewController 管理的,并且在此过程中会调用一些回调方法,例如willTransitionTo、didFinishAnimating 等。
当我们从第一页或第零个索引向后滑动时,是否可以关闭 UIPageViewController?
我检查并发现此操作没有调用任何回调方法。
【问题讨论】:
标签: ios swipe uipageviewcontroller
我知道默认情况下滑动是由UIPageViewController 管理的,并且在此过程中会调用一些回调方法,例如willTransitionTo、didFinishAnimating 等。
当我们从第一页或第零个索引向后滑动时,是否可以关闭 UIPageViewController?
我检查并发现此操作没有调用任何回调方法。
【问题讨论】:
标签: ios swipe uipageviewcontroller
我能够达到如下预期的效果。
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 只有在我们自己的回扫手势识别器失败时才会被触发。
【讨论】:
按照设计,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,因为它是内部UIScrollView,然后检查内容偏移量以查看用户滚动了多远。 stackoverflow.com/questions/22577929/…
如果您的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
...
}
...
}
【讨论】: