I am making an app pdf reader using PDFKit but I am unable to get the current page number. I can get the total pages by pdfView.document?.pageCount
. The alternative way we can use for this is to change the page by button and count it but I want the PDFView default feature Changing the page by Swipe by sitting the pdfView.usePageViewController(true)
but it does not give any method to get the current page number
Code
struct ContentView: View {
let url = Bundle.main.url(forResource: "file", withExtension: "pdf")
var body: some View {
VStack{
PDFKitRepresentedView(data: try! Data(contentsOf: url!))
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
import PDFKit
import SwiftUI
struct PDFKitRepresentedView: UIViewRepresentable {
typealias UIViewType = PDFView
let data: Data
func makeUIView(context _: UIViewRepresentableContext<PDFKitRepresentedView>) -> UIViewType {
// Create a `PDFView` and set its `PDFDocument`.
let pdfView = PDFView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
pdfView.document = PDFDocument(data: data)
pdfView.backgroundColor = UIColor.red
pdfView.displayMode = .singlePage
pdfView.displayDirection = .horizontal
pdfView.usePageViewController(true)
pdfView.maxScaleFactor = pdfView.scaleFactorForSizeToFit
pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
pdfView.autoScales = true
return pdfView
}
func updateUIView(_ pdfView: UIViewType, context _: UIViewRepresentableContext<PDFKitRepresentedView>) {
pdfView.document = PDFDocument(data: data)
}
}
Update
According to suggestion given by workingdog support Ukraine below the coordinator class printing the result but when I use Binding to pass the currentPage to SwiftUI its not working the page number is not updating in UI and on swiping its repeating the first two pages only
New Updated code
struct ContentView: View {
@State var currentPage = -1
@State var totalPages :Int?
let url = Bundle.main.url(forResource: "file", withExtension: "pdf")
var body: some View {
VStack{
HStack{
Text("\(currentPage)/")
Text("\(totalPages ?? 0)")
}
if let url = url {
PDFKitRepresentedView(data:try! Data(contentsOf: url),totalPages: $totalPages,currentPage: $currentPage)
}
}
}
}
struct PDFKitRepresentedView: UIViewRepresentable {
typealias UIViewType = PDFView
let data: Data
@Binding var totalPages:Int?
@Binding var currentPage :Int
let pdfView = PDFView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
func makeUIView(context: Context) -> UIViewType {
pdfView.document = PDFDocument(data: data)
pdfView.backgroundColor = UIColor.red
pdfView.displayMode = .singlePage
pdfView.displayDirection = .horizontal
pdfView.usePageViewController(true)
pdfView.maxScaleFactor = pdfView.scaleFactorForSizeToFit
pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
pdfView.autoScales = true
pdfView.delegate = context.coordinator
return pdfView
}
func updateUIView(_ pdfView: UIViewType, context _: Context) {
pdfView.document = PDFDocument(data: data)
DispatchQueue.main.async {
totalPages = pdfView.document?.pageCount
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(self, cp: $currentPage)
}
class Coordinator: NSObject, PDFViewDelegate {
var parent: PDFKitRepresentedView
@Binding var currentPage :Int
init(_ parent: PDFKitRepresentedView,cp:Binding<Int>) {
self.parent = parent
_currentPage = cp
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(pageChangeHandler(_:)), name: .PDFViewPageChanged, object: nil)
}
@objc func pageChangeHandler(_ notification: Notification) {
if let thePage = parent.pdfView.currentPage,
let ndx = parent.pdfView.document?.index(for: thePage),
currentPage != ndx {
DispatchQueue.main.async {
self.currentPage = ndx
}
print("--------> currentPageIndex: \(ndx) ")
}
}
}
}
According to the docs at: https://developer.apple.com/documentation/pdfkit/pdfview there is a currentPage
that returns the current page. You could then use something like this to get the index of it:
if let thePage = pdfView.currentPage, let ndx = pdfView.document?.index(for: thePage) {
print("--> currentPageIndex: \(ndx) ")
// ....
}
EDIT-1:
Try the following approach, using a Coordinator
class for the PDFViewDelegate
and getting notified when a .PDFViewPageChanged
with a NotificationCenter.default.addObserver(...)
.
You will have to adjust the code for your own purpose.
struct PDFKitRepresentedView: UIViewRepresentable {
typealias UIViewType = PDFView
let data: Data
let pdfView = PDFView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
func makeUIView(context: Context) -> UIViewType {
pdfView.document = PDFDocument(data: data)
pdfView.backgroundColor = UIColor.red
pdfView.displayMode = .singlePage
pdfView.displayDirection = .horizontal
pdfView.usePageViewController(true)
pdfView.maxScaleFactor = pdfView.scaleFactorForSizeToFit
pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
pdfView.autoScales = true
pdfView.delegate = context.coordinator
return pdfView
}
func updateUIView(_ pdfView: UIViewType, context _: Context) {
pdfView.document = PDFDocument(data: data)
}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
class Coordinator: NSObject, PDFViewDelegate {
var parent: PDFKitRepresentedView
var prevPage = -1
init(_ parent: PDFKitRepresentedView) {
self.parent = parent
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(pageChangeHandler(_:)), name: .PDFViewPageChanged, object: nil)
}
@objc func pageChangeHandler(_ notification: Notification) {
if let thePage = parent.pdfView.currentPage,
let ndx = parent.pdfView.document?.index(for: thePage),
prevPage != ndx {
print("--------> currentPageIndex: \(ndx) ")
prevPage = ndx
}
}
}
}
EDIT-2:
To access the currentPage
in ContentView
, that is, outside the PDFViewer
,
you can use the following approach.
It uses a .onReceive(...)
of a page change notification, and some minor changes of
the original code.
struct ContentView: View {
@State var currentPage = 0
let pdfViewer: PDFViewer // <--- here
init() {
if let url = Bundle.main.url(forResource: "file", withExtension: "pdf"),
let docData = try? Data(contentsOf: url) {
self.pdfViewer = PDFViewer(data: docData)
} else {
self.pdfViewer = PDFViewer(data: Data())
}
}
var body: some View {
VStack {
Text("page \(currentPage)")
pdfViewer
.onReceive(NotificationCenter.default.publisher(for: .PDFViewPageChanged)) { _ in
if let thePage = pdfViewer.pdfView.currentPage,
let ndx = pdfViewer.pdfView.document?.index(for: thePage), currentPage != ndx {
currentPage = ndx
}
}
}
}
}
struct PDFViewer: UIViewRepresentable {
typealias UIViewType = PDFView
let data: Data
let pdfView = PDFView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
func makeUIView(context: Context) -> UIViewType {
pdfView.document = PDFDocument(data: data)
pdfView.backgroundColor = UIColor.red
pdfView.displayMode = .singlePage
pdfView.displayDirection = .horizontal
pdfView.usePageViewController(true)
pdfView.maxScaleFactor = pdfView.scaleFactorForSizeToFit
pdfView.minScaleFactor = pdfView.scaleFactorForSizeToFit
pdfView.autoScales = true
return pdfView
}
func updateUIView(_ pdfView: UIViewType, context _: Context) {
pdfView.document = PDFDocument(data: data)
}
}