Most articles on showing sheets in SwiftUI apps show an example of a button with a .sheet
modifier that shows the sheet. But if you try to apply the example to a menu in a Mac app,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
struct PreviewMenu: View { @State private var isPreviewing = false var body: some View { Button(action: { isPreviewing = true }, label: { Text("Preview") }) .sheet(isPresented: $isPreviewing) { PreviewView() } } } |
The sheet does not appear when you choose the menu item. How do you show the sheet when someone chooses the menu item?
- Add a focused value for showing the sheet.
- Set the focused scene value in your main view.
- Add the menu item to show the sheet.
- Add the menu item to the app’s menu bar.
Read the Accessing the Document in a SwiftUI Menu article to learn more about focused values and working with SwiftUI menus.
Add a Focused Value
Add a focused value that your app uses to control whether or not to show the sheet. In this article I’m going to use the example of a menu item that previews some content in a sheet.
1 2 3 4 5 6 7 8 9 10 |
struct ShowPreviewKey: FocusedValueKey { typealias Value = Binding<Bool> } extension FocusedValues { var showPreview: Binding<Bool>? { get { self[ShowPreviewKey.self] } set { self[ShowPreviewKey.self] = newValue } } |
Set the Focused Scene Value
After creating the focused value to show the sheet, you must make that value a focused scene value to show the sheet from a menu. Start by adding a property to the main SwiftUI view.
1 2 |
@State private var showingPreview: Bool = false |
The next step is to add a .focusedSceneValue
modifier to the view. The first argument is the name of the variable you created for the focused value. The second argument is a binding using the property you created in the view.
1 2 3 4 5 |
.focusedSceneValue(\.showPreview, $showingPreview) .sheet(isPresented: $showingPreview) { PreviewView() } |
Apple added the .focusedSceneValue
modifier in macOS 12.
Add the Menu Item
Menus in SwiftUI apps are SwiftUI views. To show a sheet from the menu, start by creating a property with the @FocusedValue
property wrapper that contains the focused value to show the sheet.
Create a button for the menu item. The action for the button is to set the focused value’s wrapped value. The focused value for showing a sheet is a Boolean value so you give it the value of true, which shows the sheet when someone chooses the menu item.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
struct PreviewMenu: View { @FocusedValue(\.showPreview) private var showPreview var body: some View { Button(action: { showPreview?.wrappedValue = true }, label: { Text("Preview") }) .disabled(showPreview == nil) } } |
Add the Menu to the App
After creating the menu you must add it to your app’s menu bar. Add a .commands
modifier to the app’s window group or document group to add the menu to the menu bar.
1 2 3 4 5 6 7 |
.commands { // Replace .newItem with wherever you want to insert your menu item. CommandGroup(after: .newItem) { PreviewMenu() } } |
Credits
Thanks to Jason Armstrong for his answer to the following Stack Overflow question: