Reputation: 87
I know the swipe is managed by UIPageViewController
by default and there are callback methods which are called during this process like willTransitionTo
, didFinishAnimating
etc.
Is it possible to dismiss the UIPageViewController
when we swipe back from first page or zeroth Index?
I checked and found none of the callback methods are called for this action.
Upvotes: 0
Views: 1634
Reputation: 184
If your UIPageViewController
is in a container view, you need to pass the UINavigationController
or handle it inside the container's view controller:
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
}
In container view controller:
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
...
}
...
}
Upvotes: 0
Reputation: 2412
I was able to achieve the intended effect as follows.
1) Add the following properties to your UIPageViewController subclass:
var scrollView: UIScrollView?
var swipeBackPanGestureRecognizer: UIPanGestureRecognizer?
2) Now add the following code to your UIPageViewController subclass' viewDidLoad method:
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
}
Basically you are adding a new UIPanGestureRecognizer to the build in scrollView that takes over the target actions from the built in interactivePopGestureRecognizer, responsible for the swipe back (I found this tip here: https://stackoverflow.com/a/57487641/10060753).
3) Finally you need to implement these two protocol methods. Make sure to add the following code below the last closing bracket of your UIPageViewController subclass and also to replace "YourPageViewController" with the appropriate subclass name:
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
}
}
}
The implementation of gestureRecognizerShouldBegin(_:) ensures that our swipe back gesture only gets active if we are a) on the first page (UIPageViewControllerDataSource viewControllerBefore == nil) and b) if we are swiping from left to right (velocity > 0).
The implementation of gestureRecognizer(_: shouldBeRequiredToFailBy:) ensures that the built-in UIPageViewController panGestureRecognizer only gets triggered if our own swipe back gesture recognizer fails.
Upvotes: 3
Reputation: 477
By design UIPageViewController
is not aware of the current view controller index. You can create a new instance of UIViewController
every time pageViewController(_:viewControllerAfter:)
is called and you will have infinite number of pages forward.
I suspect that you provide some kind of data source to UIPageViewController, either by it's data source or by setViewControllers(_:direction:animated:completion:)
. So it's your responsibility to track the index of the currently displayed UIViewController
.
You can check what view controller is being shown by accessing first element of the previousViewControllers
in UIPageViewController
's delegate:
func pageViewController(_ pageViewController: UIPageViewController,
didFinishAnimating finished: Bool,
previousViewControllers: [UIViewController],
transitionCompleted completed: Bool)
Upvotes: 0