I have a scroll transition style UIPageViewController
that needs to disable paging only when device is in landscape orientation. But paging should be enabled in portrait orientation.
I have encountered similar questions here in SO but not my specific need. Some of them are:
How do I Disable the swipe gesture of UIPageViewController?
Disable Page scrolling in UIPageViewController
Disable/enable scrolling in UIPageViewController
Restrict UIPageViewController (with TransitionStyleScroll) pan gesture to a certain area
All of the above points to either completely disabling or restricting pan gesture to a certain area.
I will need to find that certain area
That certain area (described in previous point) needs to be calculated differently for portrait & landscape orientation
Certain area for portrait orientation needs to be the area of the
whole UIPageViewController
bounds
Certain area for landscape orientation needs to be a very minimum area
(whose frame could be 0
, 0
, 1
, 1
) where user won't be able to
perform pan operation. This frame calculation needs to be very
precise because my UIPageViewController
takes the whole bounds of
the main screen in landscape orientation.
Then again may need to track device orientation change for different calculation of the certain area
There are some techniques where the authors suggest:
pvc.dataSource = nil // prevents paging
pvc.dataSource = `a valid dataSource object` // enables paging
So, manual enable + disable again. Track orientation change and enable/disable.
This isn't safe to use for my specific use case as there is a possibility of assigning data source multiple times.
There are other approaches which, I think, can't be modified to fit the use case.
Is there a shortcut way to achieve what I need?
Answering to my own question as I've already achieved what I needed to.
Subclassing UIPageViewController
is the easiest way. We have to find the underlying UIScrollView
that is used by the page view controller to handle its pan gesture related work. We will add another UIPanGestureRecognizer
to that internal scroll view. This pan gesture recognizer won't perform any action essentially but it will block the internal pan gesture recognizer to be recognized for landscape orientation only.
Sample implementation:
class CustomPageViewController: UIPageViewController, UIGestureRecognizerDelegate {
override func viewDidLoad() {
if let underlyingScrollView = view.subviews.compactMap({ $0 as? UIScrollView })
.first {
let pangestureRecognizer = UIPanGestureRecognizer()
pangestureRecognizer.delegate = self
underlyingScrollView.addGestureRecognizer(pangestureRecognizer)
// at this point, the underlying scroll view will have two pan gesture
// recognizer side by side. We have the control of our added pan gesture
// recognizer through the delegate. We can conditionally recognize it or not
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer)
-> Bool {
// Returning true from here means, page view controller will behave as it is
// Returning false means, paging will be blocked
// As I needed to block paging only for landscape orientation, I'm just returning
// if orientation is in portrait or not
return UIApplication.shared.statusBarOrientation.isPortrait
}
}