How to show and hide a sidebar in a SwiftUI macOS app

⋅ 2 min read ⋅ SwiftUI macOS Catalyst

Table of Contents

Problem

Users can resize a sidebar by dragging it around the edge. And if they drag it far enough, the sidebar will be close, and no way to get it back.

This article will tell you how to mitigate this problem by adding other options to show and hide the sidebar.

Here is the problem in action. Once the sidebar is collapsed, there is no way to recover from it. Drag the left edge would resize the window, not bring the sidebar back.

Drag the sidebar until collapsed and there is no way to get it back.
Drag the sidebar until collapsed and there is no way to get it back.

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Solutions

There are two ways to tackle this problem.

  1. Add a menu to show and hide the sidebar.
  2. Add a toolbar button to toggle it.

Add Toggle Sidebar menu option

To add a new menu on macOS, we add desired command to .commands modifier. The command we need in this situation is SidebarCommands.

@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}.commands {
SidebarCommands() // 1
}
}
}

<1> Pass SidebarCommands to the commands modifier to add a set of commands for manipulating sidebars.

After adding this, you will get the Toggle Sidebar menu under the View menu. User can expand their lost sidebar by going to View menu > Toggle Sidebar or using shortcut ⌘ - command + ⌥ - option + S.

Toggle Sidebar menu.
Toggle Sidebar menu.
Use menu or shortcut to toggle sidebar menu.
Use menu or shortcut to toggle sidebar menu.

Add Toggle Sidebar toolbar button

Menu and a shortcut might not be obvious to users. I think the better way is to add a UI component for this action. There is no SwiftUI interface to do this, so we rely on the AppKit interface.

We add a button to a toolbar that call the toggleSidebar method.

struct ContentView: View {
var body: some View {
NavigationView {
Text("Sidebar")
Text("Content")
}.toolbar {
ToolbarItem(placement: .navigation) {
Button(action: toggleSidebar, label: { // 1
Image(systemName: "sidebar.leading")
})
}
}
}

private func toggleSidebar() { // 2
#if os(iOS)
#else
NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
#endif
}
}

<1> Add a button to a toolbar.
<2> Call toggleSidebar on a first responder.

You will get a button that toggles the sidebar when tap. The button sits right around the context. It is easier to reach than the menu option.

Add toggle sidebar button to the toolbar.
Add toggle sidebar button to the toolbar.

You can easily support sarunw.com by checking out this sponsor.

Sponsor sarunw.com and reach thousands of iOS developers.

Conclusion

I think it is good to implement this in your app if your app contains a sidebar. Otherwise, your users might not be able to access the sidebar if they accidentally close it. The sidebar closing state also survives the app restart, which makes it impossible to recover.

If you find a better way to do this, please let me know. I'm still new to macOS and SwiftUI. My Twitter direct message and email are always open.


Read more article about SwiftUI, macOS, Catalyst, or see all available topic

Enjoy the read?

If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron Buy me a coffee Tweet Share
Previous
Dynamic button configuration in iOS 15

Learn how to change button configuration, e.g., title and color, based on the internal and external changes.

Next
How to explicitly specialize a generic function in Swift

Learn a workaround to specify a type for your generic functions.

← Home