Toggle Sidebar in SwiftUI NavigationView on macOS
While you could try to perform #selector(NSSplitViewController.toggleSidebar(_:))
on keyWindow?.contentViewController
or keyWindow?.firstResponder?
, it appears that this doesn't work consistently in some situations.
Instead, you can use this command:
NSApp.sendAction(#selector(NSSplitViewController.toggleSidebar(_:)), to: nil, from: nil)
It sends the toggleSidebar
selector to the first object that can react to it, meaning, the only sidebar in the current window. This behavior is better documented on Apple's documentation website.
This method is the default implementation used by the SidebarCommands()
menu item. This is found by adding the Toggle Sidebar menu item, then fetching it's selector like so:
let menu = NSApp.mainMenu!.items.first(where: { $0.title == "View" })!
let submenu = menu.submenu!.items.first(where: { $0.title == "Toggle Sidebar" })!
submenu.target // nil
submenu.action // #selector(toggleSidebar:)
This means that it will most likely be more consistent (and supported) than the other methods.
Since macOS Big Sur beta 4 you can add default sidebar commands with SwiftUI 2.0.
var body: some Scene {
WindowGroup {
NavigationView {
Group {
SidebarView()
ContentView()
}
}
}
.commands {
SidebarCommands()
}
}
This code will add the "Toggle Sidebar" shortcut:
SidebarView code:
var body: some View {
List {
ForEach(0..<5) { index in
Text("\(index)")
}
}
.listStyle(SidebarListStyle())
}
In SwiftUI 2.0 you can use NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
with a toolbar button like this:
.toolbar {
ToolbarItem(placement: .navigation) {
#if os(macOS)
Button(action: toggleSidebar, label: {
Image(systemName: "sidebar.left")
})
#endif
}
}
func toggleSidebar() {
#if os(macOS)
NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
#endif
}