How to Create a Single Page Vertical Scrolling Pdfview in Swift

How to resolve the conflict of gestures between PDFView and SCrollView [Swift5, iOS 14]

Gesture Recognizers are working as a chain or pipeline that processes touches - after one (G1) fails, second one (G2) tries to recognize its gesture. Here you have at least 4 recognizers - your 2 ones (left and right), and the 2 scrollView's ones (pan and pinch). I will give the brief solution that covers only scrollView's pan recognizer, if you'll see problems also with pinch - you'll need to follow the same approach.

Let's say G1 is your left recognizer, and G2 is scrollView's pan recognizer.
In order to make G2 process the same touches as G1, they should be told to recognize simultaneously.

Also, the user might move his/her finger a bit horizontally while scrolling vertically, so in that case, you also want scrolling to start only after your G1 gives up on swipe and fails to recognize it.

In order to achieve that, you should add this code to your VC.

override func viewDidLoad(){
super.viewDidLoad()

...

leftSwipeGesture.delegate = self
leftSwipeGesture.cancelsTouchesInView = false
rightSwipeGesture.delegate = self
rightSwipeGesture.cancelsTouchesInView = false
}

optional func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return gestureRecognizer == leftSwipeGesture
|| gestureRecognizer == rightSwipeGesture
|| otherGestureRecognizer == leftSwipeGesture
|| otherGestureRecognizer == rightSwipeGesture
}

optional func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
guard let _ = gestureRecognizer as? UIPanGestureRecognizer else { return false }
return otherGestureRecognizer == leftSwipeGesture
|| otherGestureRecognizer == rightSwipeGesture
}

optional func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
guard let _ = otherGestureRecognizer as? UIPanGestureRecognizer else { return false }
return gestureRecognizer == leftSwipeGesture
|| gestureRecognizer == rightSwipeGesture
}

If UIGestureRecognizerDelegate methods that I added are not getting called, you'll need to create a subclass PDFView, make left/rightSwipeGesture.delegate = pdfView and override in your PDFView subclass its UIGestureRecognizerDelegate methods with this logic.

iOS PDFKit adjust pdf frame in PDFView

New Hack:

So it turns out, after digging through some more private APIs (yes, this solution is still gross) that there is this magical property that I had completely overlooked on PDFScrollView (a private, internal view) called... /p>

forcesTopAlignment /p>

To enable this, find the PDFScrollView in your PDFView and:

pdfScrollView.setValue(true, forKey: "forcesTopAlignment")

That does the trick!



Old Hack:

After a few days up against the wall I finally figured out a way to get PDFView to behave how I want.

Disclosure: This solution uses method swizzling and relies on private view hierarchy and could break at any time. That said, it works for now and I'm happy with the solution.

The full solution is available in this gist. (The meat is in overrideCenterAlign.)

There is a private method aptly named _centerAlign which vertically centers and scales the pdf pages as they come onto the screen and as they're scaled with the pinch gesture. Swizzling that method allows me to inject my own logic and apply my own transforms to position the pdf view how I'd like (with the top of the page aligned to the top of the view.)

There are two cases to consider. The "short page" case (pages 1, 3, 4 in the example) and the "tall page" case (page 2 in the example.) In both cases I start by invoking the original implementation of _centerAlign so that it can apply its transforms and manage updating the internal scroll view.

  • For the "short page" case, I apply the same transform with a vertical translation to align the top of the pdf with the top of the view.
  • For the "long page" case, I adjust the internal scroll view's zoom scale as it comes onto the screen so that it's scaled to fit the width of the view.

All of that said, I'm open to cleaner solutions that don't rely on private methods and view hierarchy. If you know of a better way to accomplish this, please post an answer!

iOS PDFKit Disable vertical scroll bounce

Unfortunately, there isn't an exported API to set the PDFView desired bouncing behavior.

Having said that, you can (safely) exploit a PDFView implementation detail to hack your way around it for now:

extension PDFView {

/// Disables the PDFView default bouncing behavior.
func disableBouncing() {
for subview in subviews {
if let scrollView = subview as? UIScrollView {
scrollView.bounces = false
return
}
}
print("PDFView.disableBouncing: FAILED!")
}
}

and then use it like this in your code:

pdfView.disableBouncing()

Caveat. Please keep in mind that such solution might break in future iOS releases. Nevertheless, rest assured your app won't crash as a result (you only won't be disabling the bouncing behavior at all).



Related Topics



Leave a reply



Submit