I am adding a sort menu to my macOS SwiftUI app. Some of the items can have checkmarks next to them. For each menu item I create a Toggle item bound to an @State Bool. When an item is clicked I would like to run additional code to update the sort, so I added an .onChange, but it is never called. The checkmark for the item appears and disappears as I would expect, but the .onChange never gets hit.
Here is an example of creating a macOS menu item with checkmark. The "change" is never printed and a breakpoint set there never gets hit.
import SwiftUI
struct ViewMenuCommands: Commands {
@State
var isAlphabetical = false
@CommandsBuilder var body: some Commands {
CommandMenu("ViewTest") {
Toggle("Alphabetical", isOn: $isAlphabetical)
.onChange(of: isAlphabetical) { value in
print( "change" )
}
}
}
}
And here's where I add the menu to the app:
import SwiftUI
@main
struct MenuToggleTestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.commands {
ViewMenuCommands()
}
}
}
CodePudding user response:
The problem is a result of @State
not notifying Commands
of its change -- @State
will only notify a View
of a change.
The solution is to wrap your menu item in a view that can respond to changes (via either @State
or an ObservableObject
-- I've chosen then latter).
Example:
@main
struct MainApp: App {
@StateObject var menuState = MenuState()
var body: some Scene {
WindowGroup {
ContentView()
}
.commands {
CommandMenu("ViewTest") {
AlphabeticalMenuItem(menuState: menuState)
}
}
}
}
class MenuState : ObservableObject {
@Published var isAlphabetical = false
}
struct AlphabeticalMenuItem : View {
@ObservedObject var menuState : MenuState
var body: some View {
Toggle("Alphabetical", isOn: $menuState.isAlphabetical)
.onChange(of: menuState.isAlphabetical) { value in
print( "change" )
}
}
}