I have a very simple sample macOS application with one custom menu command just in order to test my ideas as follows.
import SwiftUI
@main
struct MenuMonsterMacApp: App {
@State var fileOpenEnabled: Bool = true
var body: some Scene {
WindowGroup {
ContentView()
.frame(width: 480.0, height: 320.0)
}.commands {
CommandGroup(after: .newItem) {
Button {
print("Open file, will you?")
} label: {
Text("Open...")
}
.keyboardShortcut("O")
.disabled(false)
}
}
}
}
And I want to enable and disable this command with a click of a button that is placed in ContentView
. So I've created an ObservableObject
class to observe the boolean value of the File Open command as follows.
import SwiftUI
@main
struct MenuMonsterMacApp: App {
@ObservedObject var menuObservable = MenuObservable()
@State var fileOpenEnabled: Bool = true
var body: some Scene {
WindowGroup {
ContentView()
.frame(width: 480.0, height: 320.0)
}.commands {
CommandGroup(after: .newItem) {
Button {
print("Open file, will you?")
} label: {
Text("Open...")
}
.keyboardShortcut("O")
.disabled(!fileOpenEnabled)
}
}.onChange(of: menuObservable.fileOpen) { newValue in
fileOpenEnabled = newValue
}
}
}
class MenuObservable: ObservableObject {
@Published var fileOpen: Bool = true
}
In my ContentView
, which virtually runs the show, I have the following.
import SwiftUI
struct ContentView: View {
@StateObject var menuObservable = MenuObservable()
var body: some View {
VStack {
Button {
menuObservable.fileOpen.toggle()
} label: {
Text("Click to disable 'File Open'")
}
}
}
}
If I click on the button, the boolean status of the menu command in question won't change. Is this a wrong approach? If it is, how can enable and disable the menu command from ContentView
? Thanks.
To enable and disable the command with a click of a button that is placed in ContentView
,
try the following approach, using passing environmentObject
and a separate View for the
menu Button.
import SwiftUI
@main
struct MenuMonsterMacApp: App {
@StateObject var menuObservable = MenuObservable()
var body: some Scene {
WindowGroup {
ContentView().environmentObject(menuObservable)
.frame(width: 480.0, height: 320.0)
}.commands {
CommandGroup(after: .newItem) {
OpenCommand().environmentObject(menuObservable)
}
}
}
}
struct OpenCommand: View {
@EnvironmentObject var menuObservable: MenuObservable
var body: some View {
Button {
print("Open file, will you?")
} label: {
Text("Open...")
}
.disabled(!menuObservable.fileOpen)
.keyboardShortcut("O")
}
}
class MenuObservable: ObservableObject {
@Published var fileOpen: Bool = true
}
struct ContentView: View {
@EnvironmentObject var menuObservable: MenuObservable
var body: some View {
VStack {
Button {
menuObservable.fileOpen.toggle()
} label: {
Text("Click to disable 'File Open'")
}
}
}
}