Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I show QLPreviewPanel with SwiftUI on macOS?

Trying to figure out how to work with QuickLook in SwiftUI on both iOS and macOS. I suspect that in far future, there will be some unified SwiftUI QL API, but can’t see it in sight yet, so let’s work with what we have…

How do I present and configure a QLPreviewPanel from my SwiftUI view? So far, I have this:

struct ItemView: View {
    let previewPanelThing = PreviewPanelThing()
    var body: some View {
        Button("OSX preview") {
            print("osx preview")
            if let previewPanel = QLPreviewPanel.shared() {
                self.previewPanelThing.updateControllerForPanel(previewPanel)
                previewPanel.makeKeyAndOrderFront(self.previewPanelThing)
            }                   
        }
    }
}

class PreviewPanelThing: QLPreviewPanelDataSource {

    func updateControllerForPanel(_ panel: QLPreviewPanel) {
        print("updating controller")
        panel.updateController()
    }

    func numberOfPreviewItems(in panel: QLPreviewPanel!) -> Int {
        print("number of items")
        return 1
    }

    func previewPanel(_ panel: QLPreviewPanel!, previewItemAt index: Int) -> QLPreviewItem! {
        print("requesting preview item")
        let fileURL: URL = Bundle.main.url(forResource: "Thinking-of-getting-a-cat", withExtension: "png")!
        return fileURL as QLPreviewItem
    }   
}

This isn’t working. I suspect this is because the QLPreviewPanel documentation says: The preview panel follows the responder chain and adapts to the first responder willing to control it. My previewPanelThing instance isn’t in the UI and responder chain. I’m not sure how the responder chain works in SwiftUI and how to best go about it.

like image 901
Jaanus Avatar asked Oct 16 '25 15:10

Jaanus


1 Answers

Here is possible approach on using QLPreviewView directly to preview PDF files (in this demo stored in main application bundle, but this does not change the common idea)

Update: added variant with QLPreviewPanel on button click

import SwiftUI
import AppKit
import Quartz

func loadPreviewItem(with name: String) -> NSURL {

    let file = name.components(separatedBy: ".")
    let path = Bundle.main.path(forResource: file.first!, ofType: file.last!)
    let url = NSURL(fileURLWithPath: path!)

    return url
}

struct MyPreview: NSViewRepresentable {
    var fileName: String

    func makeNSView(context: NSViewRepresentableContext<MyPreview>) -> QLPreviewView {
        let preview = QLPreviewView(frame: .zero, style: .normal)
        preview?.autostarts = true
        preview?.previewItem = loadPreviewItem(with: fileName) as QLPreviewItem
        return preview ?? QLPreviewView()
    }

    func updateNSView(_ nsView: QLPreviewView, context: NSViewRepresentableContext<MyPreview>) {
    }

    typealias NSViewType = QLPreviewView

}

struct ContentView: View {
    let qlCoordinator = QLCoordinator()

    var body: some View {

        // example.pdf is expected in app bundle resources
        VStack {
            MyPreview(fileName: "example.pdf")
            Divider()
            Button("Show panel") {
                let panel = QLPreviewPanel.shared()
                panel?.center()
                panel?.dataSource = self.qlCoordinator
                panel?.makeKeyAndOrderFront(nil)
            }
        }
    }

    class QLCoordinator: NSObject, QLPreviewPanelDataSource {
        func previewPanel(_ panel: QLPreviewPanel!, previewItemAt index: Int) -> QLPreviewItem! {
            return loadPreviewItem(with: "example.pdf") as QLPreviewItem
        }

        func numberOfPreviewItems(in controller: QLPreviewPanel) -> Int {
            return 1
        }
    }
}
like image 114
Asperi Avatar answered Oct 18 '25 08:10

Asperi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!