Search code examples
swiftuipdfkit

Swift UI and PDFKit - How do I update my page programmatically?


I want to update the view when the State of currentpage changes by a button. In console the tap make current page increase accordingly.

I tried with gotoNextPage() (SOLUTION 2)and it is not working and also tried with go to method.(SOLUTION 1) Non of them are updating even if printed values seems to be ok.

PDFVIEW CurrentPage value is:Optional(<PDFPage: 0x281917b60> page index 203)

Any solution, please?

@State var pdfName: String
@State var start: Int

PDFKitView(url: Bundle.main.url(forResource: pdfName, withExtension: "pdf")!, currentPage: start)
import SwiftUI  
import PDFKit

struct PDFKitView: View {
    var url: URL
    var currentPage: Int
    
    var body: some View {
        PDFKitRepresentedView(url, currentPage)
    }
}

struct PDFKitRepresentedView: UIViewRepresentable {
    let url: URL
    let currentPage: Int
    let pdfView = PDFView()

    init(_ url: URL, _ currentPage: Int) {
        self.url = url
        self.currentPage = currentPage
    }

    func makeUIView(context: UIViewRepresentableContext<PDFKitRepresentedView>) -> PDFKitRepresentedView.UIViewType {
        print("PDFVIEW IS CREATED")
        pdfView.document = PDFDocument(url: self.url)
        pdfView.displayMode = .singlePage
        pdfView.displayDirection = .horizontal
        pdfView.autoScales = true
        pdfView.usePageViewController(true)
        pdfView.go(to: pdfView.document!.page(at: currentPage)!)
        
        let total = pdfView.document?.pageCount
        print("Total pages: \(total ?? 0)")
        return pdfView
    }

    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PDFKitRepresentedView>) {
        // Update the view.
             
        //THIS IS PRINTED IN CONSOLE EVERY TIME  
        print("PDFVIEW IS UPDATED")
        print("CurrentPage value is:\(currentPage)")
        
        //SOLUTION TRIED 1. SAME CODE WORKS ON makeUIView: I can see the pdf, scroll it     and zoom. Also let it start at any page I want to. But not here.
        print("PDFVIEW IS UPDATED")
        print("CurrentPage value is:\(currentPage)")
        
        //SOLUTION 1
        pdfView.document = PDFDocument(url: self.url)
        pdfView.displayMode = .singlePage
        pdfView.displayDirection = .horizontal
        pdfView.autoScales = true
        pdfView.usePageViewController(true)
        pdfView.go(to: pdfView.document!.page(at: currentPage)!)
        print("PDFVIEW CurrentPage value is:\(pdfView.currentPage)")

        /*

        PDFVIEW IS UPDATED
        CurrentPage value is:203
        PDFVIEW CurrentPage value is:Optional(<PDFPage: 0x281917b60> page index 203)
        */
      
        //SOLUTION TRIED 2
        //goToNextPage()
    }
    
    func goToNextPage(){
        pdfView.goToNextPage(nil)
    }
}

Solution

  • The source of truth should be somewhere outside and injected by binding or other observable type inside.

    Here is simplified fixed variant. Tested with Xcode 13.4 / iOS 15.5

    demo

    Main part:

        func makeUIView(context: Context) -> UIView {
            guard let document = PDFDocument(url: self.url) else { return UIView() }
    
            let pdfView = PDFView()
            print("PDFVIEW IS CREATED")
            pdfView.document = document
            pdfView.displayMode = .singlePage
            pdfView.displayDirection = .horizontal
            pdfView.autoScales = true
            pdfView.usePageViewController(true)
    
            DispatchQueue.main.async {
                self.total = document.pageCount
                print("Total pages: \(total)")
            }
            return pdfView
        }
    
        func updateUIView(_ uiView: UIView, context: Context) {
            guard let pdfView = uiView as? PDFView else { return }
    
            if currentPage < total {
                pdfView.go(to: pdfView.document!.page(at: currentPage)!)
            }
        }
    

    Note: handling PDFView delegate navigation by swipe to pass back in binding is on you.

    Complete test module is here