I find a lot of resources out there as to how to let the user select a menu item and then open a folder. The following is what I have.
import SwiftUI
@main
struct Oh_My_App: App {
var body: some Scene {
WindowGroup {
ContentView()
.frame(width: 480.0, height: 320.0)
}.commands {
CommandGroup(after: .newItem) {
Button {
if let url = showFileOpenPanel() {
print(url.path)
}
} label: {
Text("Open file...")
}
.keyboardShortcut("O")
}
}
}
func showFileOpenPanel() -> URL? {
let openPanel = NSOpenPanel()
openPanel.canChooseDirectories = true
openPanel.canCreateDirectories = false
openPanel.canChooseFiles = false
openPanel.title = "Selecting a folder..."
openPanel.message = "Please select a folder containing one or more files."
let response = openPanel.runModal()
return response == .OK ? openPanel.url : nil
}
}
Okay. That's no problem. I can print the file path. Well, my actual question is how to return this value to ContentView
? It is ContentView
that is virtually running the show in this sample application. So I use ObservableObject
as follows.
import SwiftUI
@main
struct Oh_My_App: App {
@StateObject var menuObservable = MenuObservable()
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}.commands {
CommandGroup(after: .newItem) {
Button {
menuObservable.openFile()
} label: {
Text("Open file...")
}
.keyboardShortcut("O")
}
}
}
}
class MenuObservable: ObservableObject {
@Published var fileURL: URL = URL(fileURLWithPath: "")
func openFile() {
if let openURL = showFileOpenPanel() {
fileURL = openURL
}
}
func showFileOpenPanel() -> URL? {
let openPanel = NSOpenPanel()
openPanel.canChooseDirectories = true
openPanel.canCreateDirectories = false
openPanel.canChooseFiles = false
openPanel.title = "Selecting a folder..."
openPanel.message = "Please select a folder containing one or more files."
let response = openPanel.runModal()
return response == .OK ? openPanel.url : nil
}
}
// ContentView.swift //
import SwiftUI
struct ContentView: View {
@ObservedObject var menuObservable = MenuObservable()
@State var filePath: String = ""
var body: some View {
ZStack {
VStack {
Text("Hello: \(filePath)")
}.onChange(of: menuObservable.fileURL) { newValue in
filePath = newValue.path
}
}
}
}
My ContentView
won't get updated. So how do I get ContentView
to receive a value from the menu call from App
? Thanks.
Right now, you're creating a new instance of MenuObservable
in ContentView
, so it doesn't have any connection to the instance that received the menu command. You need to pass a reference to your existing instance (ie the one owned by Oh_My_App
).
In your ContentView
, change @ObservedObject var menuObservable = MenuObservable()
to:
@ObservedObject var menuObservable : MenuObservable
And in your Oh_My_App
:
WindowGroup {
ContentView(menuObservable: menuObservable)
}