I want to unlock Button, when user scroll to the end of document. I create PDFView like:
import PDFKit
import SwiftUI
struct PDFDocumentView: UIViewRepresentable {
private var url: URL?
private let pdfDocumentDelegate: PdfDocumentDelegate = PdfDocumentDelegate()
init(url: URL) {
self.url = url
}
func makeUIView(context: Context) -> PDFView {
let pdfView = PDFView()
if let url {
pdfView.document = PDFDocument(url: url)
pdfView.autoScales = true
pdfView.setNeedsLayout()
pdfView.layoutIfNeeded()
pdfView.maxScaleFactor = 3.0
pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
}
return pdfView
}
}
As far as I know, PDFView
doesn't have a public API to do this.
If you have control over the PDF file you show, you can add a very small (in terms of page height) page at the end of the document, observe the PDFViewVisiblePagesChanged
notification, and check pdfView.visiblePages
contains the last page of pdfView.document
.
NotificationCenter.default.publisher(for: .PDFViewVisiblePagesChanged, object: pdfView)
.sink { notification in
if let pdfView = notification.object as? PDFView,
let doc = pdfView.document,
let lastPage = doc.page(at: doc.pageCount - 1),
pdfView.visiblePages.contains(lastPage) {
print("Reached bottom!")
}
}
.store(in: &cancellables)
Otherwise, you can try to find a UIScrollView
in the view hierarchy of the PDFView
, though this is not documented.
With this scroll view, you can either implement its delegate methods, scrollViewDidScroll
or scrollViewDidEndDecelerating
depending on your preference (see this answer), or use KVO to observe changes to contentOffset
(see this answer).
After you are able to detect scrolling, you can check if the scroll view is at the bottom by checking contentOffset
(see this answer).
Here is an example:
var pdfView: PDFView!
override func viewDidLoad() {
pdfView = PDFView(...)
// configure pdfView however you want...
pdfView.document = ...
// on iOS 17 at least, subviews[0] is the scroll view
// to be more future proof, consider searching recursively
scrollViewDelegate = (pdfView.subviews[0] as? UIScrollView)?.delegate
(pdfView.subviews[0] as? UIScrollView)?.delegate = self
}
var scrollViewDelegate: UIScrollViewDelegate?
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
scrollViewDelegate?.scrollViewDidEndDecelerating?(scrollView)
if scrollView.isAtBottom {
print("Reached bottom!")
}
}
// you should implement all the other UIScrollViewDelegate methods like this,
// in case PDFView needs them
func scrollViewDidScroll(_ scrollView: UIScrollView) {
scrollViewDelegate?.scrollViewDidScroll?(scrollView)
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
scrollViewDelegate?.scrollViewDidZoom?(scrollView)
}
UIScrollView.isAtBottom
is from the linked answer:
extension UIScrollView {
var isAtBottom: Bool {
return contentOffset.y >= verticalOffsetForBottom
}
var verticalOffsetForBottom: CGFloat {
let scrollViewHeight = bounds.height
let scrollContentSizeHeight = contentSize.height
let bottomInset = contentInset.bottom
let scrollViewBottomOffset = scrollContentSizeHeight + bottomInset - scrollViewHeight
return scrollViewBottomOffset
}
}