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