Home > Net >  Is there any way where we can get the current page number in PDFView and use it in SwiftUI
Is there any way where we can get the current page number in PDFView and use it in SwiftUI

Time:01-06

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

CodePudding user response:

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