Как показать QLPreviewPanel с SwiftUI в macOS?

Пытаюсь понять, как работать с QuickLook в SwiftUI как на iOS, так и на macOS. Я подозреваю, что в далеком будущем появится какой-то единый SwiftUI QL API, но пока его не видно, так что давайте поработаем с тем, что у нас есть ...

Как представить и настроить QLPreviewPanel из моего представления SwiftUI? Пока у меня есть это:

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

Это не работает. Я подозреваю, что это связано с тем, что в документации QLPreviewPanel сказано: The preview panel follows the responder chain and adapts to the first responder willing to control it. Мой previewPanelThing экземпляр не входит в цепочку пользовательского интерфейса и ответчика. Я не знаю, как цепочка респондентов работает в SwiftUI и как это лучше всего сделать.


person Jaanus    schedule 11.01.2020    source источник


Ответы (1)


Вот возможный подход к использованию QLPreviewView напрямую для предварительного просмотра файлов PDF (в этой демонстрации, хранящейся в основном пакете приложения, но это не меняет общей идеи)

Обновление: добавлен вариант с QLPreviewPanel при нажатии кнопки

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
        }
    }
}
person Asperi    schedule 11.01.2020
comment
Спасибо. Я не понимал, что могу просто напрямую назначить источник данных QLPreviewPanel. - person Jaanus; 11.01.2020
comment
Решение работает очень хорошо, но я получаю много таких сообщений: -[QLPreviewPanel setDataSource:] called while the panel has no controller - Fix this or this will raise soon. See comments in QLPreviewPanel.h for -acceptsPreviewPanelControl:/-beginPreviewPanelControl:/-endPreviewPanelControl:.. Вы знаете, как от них избавиться? - person Dominik; 13.11.2020
comment
@Asperi Я изо всех сил пытаюсь увидеть взаимосвязь между MyPreview(...) в представлении SwiftUI и QLPreviewPanel под ним в обратном вызове Button. Как эти двое связаны? - person Clifton Labrum; 23.02.2021
comment
@Dominik Я думаю, ты можешь игнорировать эти предупреждения. Я всегда получал их в чистой реализации QuickLook AppKit, поэтому не думаю, что вам стоит слишком беспокоиться. - person Clifton Labrum; 23.02.2021