SwiftUI ContextMenu with Submenu - macos

I am developing a macOS app in SwiftUI. I am using .contextMenu modifier inside my List. It all works fine, however I am trying to make a submenu.
Usually I am just using buttons, I am not trying to make a hierarchy / or a submenu. It is possible and being used in many Apple default apps. I just not sure how to create that menu. That's the code I am currently using
.contextMenu {
Button(action: {
})
{
Text("Button")
}
}
Here is a picture of the default Calendar app, with a sub menu.

You can use Menu for nested menu hierarchies:
.contextMenu {
Menu("Nested Root") {
Button("Nested #1") {}
Button("Nested #2") {}
Button("Nested #3") {}
}
Button("Not Nested") { }
}

Related

How to collapse sidebar with SwiftUI on Mac

I am building a full screen function which is supposed to hide the navigation bar and the side bar. However, I have only found a way to toggle the sidebar using NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil), which is not working well in my case because I cannot detect if the side bar is visible or not on mac (.onDisappear does not seem to be called on mac when the sidebar collapses).
Below is a simplified example of my NavigationView. I did not use NavigationSplitView because it does not work in my case unless I use some nasty hacks (which leads to crashes too).
let data: [String] = createData()
NavigationView {
List(data, id:\.self) { item in
NavigationLink {
DetailView(item)
} label: {
Text(item)
}
}
DetailView("Nothing Selected")
}

Toolbar at bottom on macOS with SwiftUI

On my macOS app, I have a main toolbar at top.
However, I would like to add a new bottom toolbar at bottom (smaller).
Like that for example:
It seems there is no way to do this on macOS as on iOS:
.toolbar {
ToolbarItemGroup(placement: .bottomBar) {
// add wine
AddButton()
Spacer()
}
}
=> .bottomBar doesn't exist on macOS.
Well, just before creating a custom view/content with some buttons, I would like to be sure there is no official way to do that on macOS (with SwiftUI only...).

SwiftUI option key alternate menu items on macOS

In many macOS apps, if you open a menu from the main menubar, you can press the option key to change some of the items.
For example in Safari, I can open the File menu, and there is a "Close Tab" item. Pressing option changes it to "Close Other Tabs".
Is there a way to do this with SwiftUI?
I know how to create basic menus, but I don't see a way to detect the option key.
I think in AppKit you use NSMenuItem's isAlternate property.
struct MyApp: App {
var body: some Scene {
WindowGroup {
...
}.commands {
CommandGroup(replacing: .newItem) {
Button { newFolder() } label: { Text("New Folder") }
.keyboardShortcut("n")
...
}
}
Update to Question
I've tried a few more things, and run into two problems.
I can connect a custom object to the end of the NSResponder chain, and the system will call its flagsChanged method when the user presses the option key. However, it will not call this method when a menu is open.
Even if I find a way to observe the option key while the menu is open, changing state used to build a CommandMenu causes the menu to disappear, not rebuild and stay open.
var body: some Commands {
// If the `isOptionDown` property changes when this menu is open,
// SwiftUI doesn't change the menu, it simply closes it.
// That's not how macOS apps usually behave.
CommandGroup(replacing: .pasteboard) {
Button(...)
if optionKeyWatcher.isOptionDown {
Button(...)
} else {
Button(...)
}

How to prevent Picker to fold into a submenu in SwiftUI macOS

I'm trying to combine Picker and some buttons inside Menu in macOS SwiftUI app. Unfortunately Picker is folding into submenu automatically and I'm struggle to find a solution. How to prevent Picker to fold, or maybe there is a better solution around?
Menu("Budgets") {
Picker("Budgets", selection: $account) {
Button("Personal") {}.tag(1)
Button("Business") {}.tag(2)
}.labelsHidden()
Divider()
Button("New Budget…") {}
Button("Manage Budgets…") {}
}
You need inline picker style, like
Picker("Budgets", selection: $account) {
Button("Personal") {}.tag(1)
Button("Business") {}.tag(2)
}
.labelsHidden()
.pickerStyle(.inline) // << here !!

SwiftUI Custom Picker / ComboBox

I am trying to get a customized Picker in SwiftUI for MacOS. The best option I would like to have is a customized view which can be pressed and then shows the Picker options like a ComboBox in AppKit.
Apple achieves that in their Contact books app on Mac or the Sharing dialog of Apple is the same way.
You press the + Button and the selection comes up. Is that possible in SwiftUI?
Edit: This looks very similar like contextMenu in SwiftUI. But how can I set it on left click instead?
Finally found MenuButton which does the trick for SwiftUI in MacOS.
MenuButton(label: Title(), content: {
Button(action: {
print("Clicked an item")
}) {
Text("Menu Item Text")
}
})
.menuButtonStyle(BorderlessButtonMenuButtonStyle())
... with using a custom View Title() for my clickable button.
struct Title: View {
var body: some View {
HStack
{
Text("Title")
}
}
}

Resources