iOS PDFKit Disable vertical scroll bounce - ios

How does one disable scroll bounce in a PDFView using PDFKit?
The view where the PDF is shown doesn't have a scroll bounce option.
Here's my code:
if let path = Bundle.main.path(forResource: pdfObject, ofType: "pdf") {
let url = URL(fileURLWithPath: path)
if let pdfDocument = PDFDocument(url: url) {
pdfView.autoresizesSubviews = true
pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight,
.flexibleTopMargin, .flexibleLeftMargin]
pdfView.autoScales = true
pdfView.displaysPageBreaks = true
pdfView.displayDirection = .vertical
pdfView.displayMode = .singlePageContinuous
pdfView.document = pdfDocument
pdfView.maxScaleFactor = 4.0
pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
}
}
Thanks in advance (for what is likely a ridiculously simple question!)

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

How to present a single page pdf document in PDFView so that the document is centred and fully presented?

I have an application that takes a photo of a document and then presented as a pdf in PDFView in a new UIViewController. The problem that I am having is that when the pdf document is presented, it is some what zoomed in and the PDFView does not show the full outline of the document by default. How do I accomplish this?
class ShowPDFViewController: UIViewController{
#IBOutlet weak var pdfPreview: PDFView!
var pdfDocument: PDFDocument!
override func viewDidLoad() {
super.viewDidLoad()
// PRESENT PDF DOCUMENT JUST CREATED
pdfPreview.document = pdfDocument
pdfPreview.autoScales = true
}
}
This is how the pdf document is currently presented default - often zoomed:
This is how I would like the document presented by default - full outline of document see:
I create the view from code the following way and it works (left and right side are aligned to superview left and right and height is scaled to fit)
var pdfView = PDFView(frame: view.frame)
pdfView.document = PDFDocument(url: Bundle.main.url(forResource: "doc"
, withExtension: "pdf")!)
pdfView.displayMode = .singlePage
pdfView.autoScales = true
view.addSubview(pdfView)
I have a idea.
you can create a ScrollView , add PDFView to it. put them to Controller.
you can change ScrollView.contentOffset for your need
Fix for pdfView.displayMode = .singlePageContinuous
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.main.async {
self.pdfView.subviews.forEach {
guard let scrollView = ($0 as? UIScrollView) else { return }
scrollView.setContentOffset(.zero, animated: false)
}
}
}

Xcode PDFKit: How To Close PDF File

I have built an App an I'm about to finish my project.
And I need to put a imprint as a pdf-file in my App (legal reasons), which I thought would be no problem.
But I can't figure out what I need to do to get a Close Button to just close the PDF File..
My code:
#IBAction func test(_ sender: Any) {
let pdfView = PDFView()
pdfView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(pdfView)
pdfView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
pdfView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
pdfView.topAnchor.constraint(equalTo: view.topAnchor, constant:25).isActive = true
pdfView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
guard let path = Bundle.main.url(forResource: "ImpressumDatenschutz", withExtension: "pdf") else { return }
if let document = PDFDocument(url: path) {
pdfView.document = document
}
}
I didn't find any other option than putting a button in the upper left corner with a segue that goes back to the page that was open before the PDF was opened. I guess that is good enough for the beginning.
#IBAction func backTapped(_ sender: Any) {
performSegue(withIdentifier: "impressumToFirstVC", sender: self)
}
Create a floating button from Here and remove your pdfView by adding pdfView.removeFromSuperview() in the floating button action method.
Simple remove from its superview like below:
pdfView.removeFromSuperview()

Removing the QuickTime icon from AVPlayerViewController

I am building a screen where users can play audio files using an AVPlayerViewController. The problem is that I can't get rid of the QuickTime logo in the player view, see screenshot:
My code:
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
guard let url = URL(string: filePath!) else {
return
}
let player = AVPlayer(url: url)
let controller = AVPlayerViewController()
controller.modalPresentationStyle = .overFullScreen
controller.player = player
self.present(controller, animated: true) {
player.play()
}
} catch {
}
I've tried adding an UIImageView using controller.contentOverlayView?.addSubView(), but I can't center that properly. How do I customize the layout of the player without having to build my own interface from scratch?
What you tried is correct: just add a subview to the content overlay view. But you left out one step: you must give both the subview and the content overlay view constraints, to make them cover the player controller’s view completely.
Example (my av is your controller):
let iv = UIView()
iv.backgroundColor = .white
av.contentOverlayView!.addSubview(iv)
let v = av.contentOverlayView!
iv.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
iv.bottomAnchor.constraint(equalTo:v.bottomAnchor),
iv.topAnchor.constraint(equalTo:v.topAnchor),
iv.leadingAnchor.constraint(equalTo:v.leadingAnchor),
iv.trailingAnchor.constraint(equalTo:v.trailingAnchor),
])
NSLayoutConstraint.activate([
v.bottomAnchor.constraint(equalTo:av.view.bottomAnchor),
v.topAnchor.constraint(equalTo:av.view.topAnchor),
v.leadingAnchor.constraint(equalTo:av.view.leadingAnchor),
v.trailingAnchor.constraint(equalTo:av.view.trailingAnchor),
])
If all you want to do is remove the "Q" logo without replacing it with anything, you can get rid of this way:
(avPlayerViewController?.view.subviews.first?.subviews.first as? UIImageView)?.image = nil
But, at least for me, the "Q" logo usually only appears about 2 seconds after I've set up the player view controller. So you might need to use a timer to get the above code to run about 2 seconds after creating the player.

Embedding an AVPlayerViewController into a UIView

I am trying to play a video inside a UIView, but the video player always appears outside of the UIView which I am embedding the AVPlayerViewController into.
Below is what I have so far....
class ViewController: UIViewController {
let smallVideoPlayerViewController = AVPlayerViewController()
#IBOutlet weak var videoView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let myFileManager = FileManager.default
let mainBundle = Bundle.main
let resourcesPath = mainBundle.resourcePath!
guard let allItemsInTheBundle = try? myFileManager.contentsOfDirectory(atPath: resourcesPath) else {
return
}
let videoName = "test"
let videoPath = Bundle.main.path(forResource: videoName, ofType: "mp4")
let videoUrl = URL(fileURLWithPath: videoPath!)
smallVideoPlayerViewController.showsPlaybackControls = false
smallVideoPlayerViewController.player = AVPlayer(url: videoUrl)
videoView.addSubview(smallVideoPlayerViewController.view)
smallVideoPlayerViewController.view.frame = videoView.frame
smallVideoPlayerViewController.player?.play()
}
...
}
The background colour of UIView is set to white. As seen from the screenshot, AVPlayer is outside of the UIView.
I tried manually setting the dimensions and position of the AVPlayer, but no luck with that either.
I am using Xcode 9.2. The project has not warnings regarding to any layout issues.
How can I perfectly align the AVPlayer, so that is would appear inside the UIView.
Thanks
Change this line:
smallVideoPlayerViewController.view.frame = videoView.frame
to this:
smallVideoPlayerViewController.view.frame = videoView.bounds
The reason is because videoView.frame includes the origin in its superview which you don't want. videoView.bounds is just the size with an origin of 0,0.
Of course you might want to go further set the auto-resizing mask to keep the video player frame the same like this:
smallVideoPlayerViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
or even to go full blown auto-layout.

pdfView pinch zooming out

I am trying to move my project that was using a webview to display some pdf to a pdfView to take advantage of the latest PDFKit features.
in the webview when pinching to zoom out the document was always scaling to fill the screen. basically you could not zoom out the page it was bouncing back to fill the screen.
Now with a pdfView, I can zoom out by pinching and it does not look good at all there is no need to have the pdf page to be smaller than the screen...
Is there any way to activate the autoscale once you release your fingers from the screen. I know there is the gesture func but I am not familiar with its use.
Just to confirm that the accepted answer is the correct one, but also to highlight where the code needs to be used (as stated in the comment by answer author). i.e. the code must be used AFTER setting the pdf document:
pdfView.document = pdfDocument
pdfView.autoScales = true
pdfView.maxScaleFactor = 4.0
pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
to answer my own question, it was actually very easy...
pdfView.autoScales = true
pdfView.maxScaleFactor = 4.0
pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
This solution will automatically adjust the PDFView once you set the document property. Just use NoZoomOutPDFView instead of PDFView as your view for displaying a PDFDocument.
import Foundation
import PDFKit
final class NoZoomOutPDFView: PDFView {
init() {
super.init(frame: .zero)
NotificationCenter
.default
.addObserver(
self,
selector: #selector(update),
name: .PDFViewDocumentChanged,
object: nil
)
}
deinit {
// If your app targets iOS 9.0 and later the following line can be omitted
NotificationCenter.default.removeObserver(self)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc private func update() {
// PDF can be zoomed in but not zoomed out
DispatchQueue.main.async {
self.autoScales = true
self.maxScaleFactor = 4.0
self.minScaleFactor = self.scaleFactorForSizeToFit
}
}
}
I had the same problem and the accepted answer did not work for me. scaleFactorForSizeToFit always returned 0.0, and therefore I was still able to zoom out until nothing was visible anymore. I don't know if this is specific to my application or something changed in iOS, but I had to trigger a layout update on the PDFView before setting the scale factor. This is how it works for me on iOS15:
if let document = PDFDocument(url: URL(string: "https://www.adobe.com/pdf/pdfs/ISO32000-1PublicPatentLicense.pdf")!) {
pdfView.displayDirection = .vertical
pdfView.autoScales = true
pdfView.document = document
pdfView.setNeedsLayout()
pdfView.layoutIfNeeded()
pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
pdfView.maxScaleFactor = 4.0
}
If overriding PDFView's method is restricted, it can be done with adding an observer;
NotificationCenter.default.addObserver(self, selector: #selector(scaleChanged), name: NSNotification.Name.PDFViewScaleChanged, object: nil)
and using it like:
#objc func scaleChanged() {
self.pdfView.maxScaleFactor = 3.0
self.pdfView.minScaleFactor = self.pdfView.scaleFactorForSizeToFit
}

Resources