Xcode PDFKit: How To Close PDF File - ios

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()

Related

Swift PDFKit: Inconsistent behaviour with PDFView.currentDestination and PDFView.go(to destination: PDFDestination)

Note: The question remains unsolved for now; the marked answer provides a good workaround - it works while the application is still open. Answers are still welcomed!
Background
I'm currently developing an app that consists of a full-screen PDFView, and I want the program to remember the position in the document before the view is dismissed so the user can pick up where they've left.
Implementation
A simplified version of the app can be understood as a PDF Viewer using PDFKit.PDFView. The storyboard consists of an UIView that's connected to a PDFView class, DocumentView (which conforms to UIViewController). The view is initialised through the following process:
In viewDidLoad:
let PDF: PDFDocument = GetPDFFromServer()
DocumentView.document = PDF!
DocumentView.autoScales = true
... (other settings)
// Set PDF Destination
let Destination: PDFDestination = GetStoredDestination()
// Code with issues
DocumentView.go(to: Destination)
In viewWillDisappear:
StoreDestination(DocumentView.currentDestination)
The Issue & Tests
I realised that the code does not work as expected; the view does not return to its previous location.
Through debugging, I realised that this might be due to the inconsistent behaviour of DocumentView.go(to destination: PDFDestination) and DocumentView.currentDestination.
To ensure the bug is not introduced by errors while storing the location, the following code is used to verify the issue, with a multi-page document:
In viewDidLoad
Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in
DispatchQueue.main.async {
self.DocumentView.go(to:self.DocumentView.currentDestination!)
}
})
Expected & Observed behaviour
Expected: The location of the document should not change - the code is going to its current destination every 1 second which should have no effects. as "currentDestination" should be the "current destination of the document, per docs")
Observed: Upon execution, the page would spontaneously scroll down by a fixed offset.
The same outcome was observed on an iPadOS 14.5 simulator and an iPadOS 15 iPad Air (Gen 4).
What might have gone wrong?
It'd be great if somebody can help.
Cheers,
Lincoln
This question was originally published on the Apple Developer Forum over a week ago; No responses were heard for over a week, so I thought I might try my luck here on StackOverflow <3
I tried PDFView.go() for this scenario and I managed to get it work for some cases but found that it fails in some other scenarios such as with zoomed documents, changed orientations.
So going back to what you are trying to achieve,
I'm currently developing an app that consists of a full-screen
PDFView, and I want the program to remember the position in the
document before the view is dismissed so the user can pick up where
they've left.
this can be done from a different approach. With this approach, you need to always keep a reference to the PDFView you created. If the previous pdf needs to be loaded again, then you pass the PDFView instance you have to the viewController as it is. Otherwise you load the new pdf to the PDFView instance and pass it to the viewController.
DocumentViewController gets the PDFView when it gets initialized.
import UIKit
import PDFKit
protocol DocumentViewControllerDelegate: AnyObject {
func needsContinuePDF(continuePDF: Bool)
}
class DocumentViewController: UIViewController {
var pdfView: PDFView!
weak var delegate: DocumentViewControllerDelegate!
init(pdfView: PDFView, delegate: DocumentViewControllerDelegate){
self.pdfView = pdfView
self.delegate = delegate
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
super.loadView()
self.view.backgroundColor = .white
view.addSubview(pdfView)
NSLayoutConstraint.activate([
pdfView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
pdfView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
pdfView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
pdfView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
}
override func viewWillDisappear(_ animated: Bool) {
delegate.needsContinuePDF(continuePDF: true)
}
}
You can initialize DocumentViewController like below. MainViewController has the responsibility for initializing PDFView.
import UIKit
import PDFKit
class MainViewController: UIViewController {
var pdfView: PDFView = PDFView()
var continuePreviousPDF = false
let button = UIButton(frame: .zero)
override func loadView() {
super.loadView()
button.setTitle("View PDF", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .systemBlue
button.addTarget(self, action: #selector(openDocumentView(_:)), for: .touchUpInside)
self.view.backgroundColor = .systemGray5
self.view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.widthAnchor.constraint(equalToConstant: 100),
button.heightAnchor.constraint(equalToConstant: 50),
button.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor),
button.centerYAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerYAnchor),
])
}
#objc func openDocumentView(_ sender: UIButton) {
//open a nee PDF if not continue previous one
if !self.continuePreviousPDF {
pdfView.autoScales = true
pdfView.displayMode = .singlePageContinuous
pdfView.translatesAutoresizingMaskIntoConstraints = false
guard let path = Bundle.main.url(forResource: "sample copy", withExtension: "pdf") else { return }
if let document = PDFDocument(url: path) {
pdfView.document = document
}
}
let documentViewController = DocumentViewController(pdfView: pdfView, delegate: self)
self.present(documentViewController, animated: true, completion: nil)
}
}
extension MainViewController: DocumentViewControllerDelegate {
func needsContinuePDF(continuePDF: Bool) {
self.continuePreviousPDF = continuePDF
}
}

AVPlayerViewController ignores all touches after returning from fullscreen

I faced a very strange behaviour of AVPlayerViewController.
I use this code to add play video inside view:
class ViewShowTest:UIViewController
{
let view_for_media = UIView()
override func viewDidLoad()
{
super.viewDidLoad()
self.view.backgroundColor = .red
self.view.addSubview(view_for_media)
view_for_media.snp.makeConstraints(
{ make in
make.top.equalToSuperview().offset(100)
make.left.right.equalToSuperview()
make.height.equalTo(300)
})
setVideo()
}
func setVideo()
{
let url = URL(string: "https://www.radiantmediaplayer.com/media/bbb-360p.mp4")!
let player = AVPlayer(url: url)
let player_vc = AVPlayerViewController(nibName: nil, bundle: nil)
player_vc.player = player
self.view_for_media.addSubview(player_vc.view)
self.addChild(player_vc)
player_vc.didMove(toParent: self)
player_vc.view.frame = view_for_media.bounds
}
}
Video is playing as expected inside view. But after going to fullScreen and returning back (no matter with swipe or close button) ViewShowTest ignores all touches. I still can drag default ios Menus from top and bottom but ViewShowTest view and player_vc.view does not reacts any touches. This bug appears only on ios 12.4 and below. How can i solve this problem?

iOS PDFKit Disable vertical scroll bounce

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).

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)
}
}
}

UIBarButton Item Won't Appear in Navigation Bar

I understand there are other questions like this but so far I have not been able to get an answer that works for so this is why I am writing this.
I right now have the following code to place a leftBarButtonItem:
let user = FIRAuth.auth()?.currentUser
var imageView: UIImageView?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setupNavBar()
}
func setupNavBar() {
//left bar button item setup
let url = URL(string: (self.user?.photoURL?.absoluteString)!)
let data = try? Data(contentsOf: url!)
imageView?.image = UIImage(data: data!)
imageView?.layer.masksToBounds = true
imageView?.layer.borderWidth = 1.5
imageView?.layer.borderColor = UIColor.white.cgColor
imageView?.layer.cornerRadius=(imageView?.bounds.width)! / 2
let profileImageButton = UIBarButtonItem(image: UIImage(named: (self.user?.photoURL?.absoluteString)!), style: .plain, target: self, action:#selector(UserGroupsMainViewController.navLeftBarButtonTapped))
self.navigationController?.navigationItem.leftBarButtonItem = profileImageButton
}
func navLeftBarButtonTapped() {
}
For some reason, the button does not appear on the navigation controller. I do know for sure that I have a URL that is not nil because I tested it in my print output. Any help would be appreciated. Thanks!
Just use navigationItem not navigationController.navigationItem
Ok, I just saw your problem.
You are using a URL as the "name" of an image. It doesn't work that way.
The function UIImage(named: "blah") will load an image from the app bundle stored with the name given.
If you want to download the image from a URL then you need to have a download task run in the background and then load the image once that's done.
Try using the AlamofireImage framework also.

Resources