macosswiftuipdfkit

How to use PDFKit represented view with binding in SwiftUI


I'm exploring PDFKit with swiftUI. Here is my sample code to select a pdf file on macOS (that's work). Show a Quicklook that's works fine too and I want to show a view with pdf content with the help of a PDFKitRepresentedView (see at the end of the code).

PDFKitRepresentedView works fine at first file selection but it doesn't work if I select another file. I thing it's a binding problem but I need help to resolve it.

Thanks...

    import SwiftUI
    import UniformTypeIdentifiers
    import QuickLook
    import PDFKit
    
    struct FilePickerView: View {
    @State var filename = ""
    @State var selectFileURL: URL?
    @State var previewURL: URL?
    
    var body: some View {
        VStack {
            HStack {
                Text(filename)
                Button("Select File")
                {
                    let panel = NSOpenPanel()
                    panel.allowsMultipleSelection = false
                    panel.canChooseDirectories = false
                    panel.allowedContentTypes = [UTType.pdf].compactMap { $0 }
                    if panel.runModal() == .OK {
                        self.selectFileURL = panel.url
                        self.filename = panel.url?.lastPathComponent ?? "<none>"
                    }
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            
            if let p = selectFileURL {
                PDFKitRepresentedView(url: p)
            }
            
            Button("Preview") {
                previewURL = selectFileURL
            }.quickLookPreview($previewURL)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        FilePickerView()
    }
}

struct PDFKitRepresentedView: NSViewRepresentable {
    
    var url : URL
    func makeNSView(context: Context) -> PDFView {
        let pdfView = PDFView()
        pdfView.document = PDFDocument(url:url)
        return pdfView
    }
    
    func updateNSView(_ nsView: PDFView, context: Context) {  }
}

Solution

  • Right now, you are setting the document (and thus the URL) of the PDFView inside makeNSView, which only gets called once when the View is created. So, if the url parameter updates, nothing will ever change, since the View is already created.

    You could solve this by using updateNSView to update the document parameter of the View, but if you ever added another parameter, you would risk updating the document when that other parameter changed as well.

    I think this is a reasonable candidate for adding an explicit id to the PDFKitRepresentedView -- whenever the id changes, makeNSView will get called again since it will be a new View.

    PDFKitRepresentedView(url: p)
      .id(p)