Thanks to this answer i can see QuickLook preview of a pdf embedded in my swiftui view.
But it's only to preview the pdf that's stored in the app bundle resource. How do i use NSOpenPanel to choose a file and display it in the QLPreviewView?
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 ?? "")
print(url)
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 {
var body: some View {
// example.pdf is expected in app bundle resources
VStack {
MyPreview(fileName: "testing.pdf")
Divider()
}
Button("Select PDF") {
let openPanel = NSOpenPanel()
openPanel.allowedFileTypes = ["pdf"]
openPanel.allowsMultipleSelection = false
openPanel.canChooseDirectories = false
openPanel.canChooseFiles = true
openPanel.runModal()
}
}
}
*UPDATE
This is what i've tried but nothing happens after i choose a pdf file with the openpanel. The View is blank. I think there's something that i haven't done correctly in the updateNSView.
struct ContentView: View {
@State var filename = ""
var body: some View {
VStack {
MyPreview(fileName: filename)
Divider()
}
Button("Select PDF") {
let openPanel = NSOpenPanel()
openPanel.allowedFileTypes = ["pdf"]
openPanel.allowsMultipleSelection = false
openPanel.canChooseDirectories = false
openPanel.canChooseFiles = true
openPanel.runModal()
print(openPanel.url!.lastPathComponent)
filename = openPanel.url!.lastPathComponent
//
}
}
}
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>) {
let preview = QLPreviewView(frame: .zero, style: .normal)
preview?.refreshPreviewItem()
}
typealias NSViewType = QLPreviewView
}
*UPDATE 2
My latest attempt using now a model to update the file url after choosing one with the open panel but nothing still happens.
It updates pdfurl successfully with the file url but the QLPreviewView doesn't update with the changes in updateNSView. I'm using the refreshItemPreview() which should work but i'm not sure what i'm doing wrong here
class PDFViewModel: ObservableObject {
@Published var pdfurl = ""
}
struct MyPreview: NSViewRepresentable {
@ObservedObject var pdfVM = PDFViewModel()
func makeNSView(context: NSViewRepresentableContext<MyPreview>) -> QLPreviewView {
let preview = QLPreviewView(frame: .zero, style: .normal)
preview?.previewItem = NSURL(string: pdfVM.pdfurl)
return preview ?? QLPreviewView()
}
func updateNSView(_ nsView: QLPreviewView, context: NSViewRepresentableContext<MyPreview>) {
let preview = QLPreviewView(frame: .zero, style: .normal)
preview?.refreshPreviewItem()
}
typealias NSViewType = QLPreviewView
}
struct ContentView: View {
@ObservedObject var pdfVM = PDFViewModel()
var body: some View {
VStack {
MyPreview()
Divider()
}
Button("Select PDF") {
let openPanel = NSOpenPanel()
openPanel.allowedFileTypes = ["pdf"]
openPanel.allowsMultipleSelection = false
openPanel.canChooseDirectories = false
openPanel.canChooseFiles = true
openPanel.runModal()
pdfVM.pdfurl = "\(openPanel.url!)"
print("the url is now \(pdfVM.pdfurl)")
}
}
}
Hacky solution but it works....
I used a looped timer that monitors and sets the preview item url which is also now stored with AppStorage
class PDFViewModel: ObservableObject {
@AppStorage("pdfurl") var pdfurl: String = ""
}
struct MyPreview: NSViewRepresentable {
@ObservedObject var pdfVM = PDFViewModel()
let preview = QLPreviewView(frame: .zero, style: .normal)
func makeNSView(context: NSViewRepresentableContext<MyPreview>) -> QLPreviewView {
preview?.autostarts = true
preview?.shouldCloseWithWindow = false
preview?.previewItem = NSURL(string: pdfVM.pdfurl)
updatedocument()
return preview ?? QLPreviewView()
}
func updatedocument() {
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
let delay = DispatchTime.now() + 0.3
DispatchQueue.main.asyncAfter(deadline: delay, execute: {
preview?.previewItem = NSURL(string: pdfVM.pdfurl)
})
}
}
func updateNSView(_ nsView: QLPreviewView, context: NSViewRepresentableContext<MyPreview>) {
}
typealias NSViewType = QLPreviewView
}
struct ContentView: View {
@ObservedObject var pdfVM = PDFViewModel()
var body: some View {
VStack {
MyPreview()
Divider()
}
Button("Select Document") {
let openPanel = NSOpenPanel()
openPanel.allowedFileTypes = ["pdf", "jpg","doc","pptx", "png", "xls", "xlsx", "docx", "jpeg", "txt"]
openPanel.allowsMultipleSelection = false
openPanel.canChooseDirectories = false
openPanel.canChooseFiles = true
openPanel.begin { (result) in
if result == NSApplication.ModalResponse.OK {
if let url = openPanel.url {
pdfVM.pdfurl = "\(url)"
}
} else if result == NSApplication.ModalResponse.cancel {
}
}
}
}
}