Collapse sidebar in SwiftUI (Xcode 12)

I'm trying to do a simple application in SwiftUI taking advantage of SwiftUI 2.0's new multiplatform project template, and I wish to add the option to collapse the sidebar as many other apps do.

I've got an extremely simple sidebar I did following the Fruta example project, and I tried adding a boolean state variable that controls whether the sidebar should show or not, but that doesn't work because when the sidebar is hidden, the main view is turned translucent.

On iPadOS, a button appears on top of the sidebar that allows to collapse it. How would I do this in macOS? Is there a way to natively achieve this in SwiftUI, or I'll need to resort to a UIKit workaround? 

Please note I'm using macOS Big Sur, Xcode 12, and SwiftUI 2.0

Thanks in advance.


Accepted Reply

Until (one would hope) Apple gets around to it, here's something that'll work.

Using Debug View Hierarchy in the debug toolbar, I found that a private NSSplitViewController is being used for layout. I opted to pass an action down the responder chain to toggle its associated sidebar:

Code Block swift
    private func documentGroup() -> some Scene {
        return DocumentGroup(newDocument: Document()) {
            DocumentView()
                .toolbar {
                    ToolbarItem(placement: .navigation) {
                        Button(action: toggleSidebar, label: {
                            Image(systemName: "sidebar.left")
                        })
                    }
                }
                .environmentObject($0.document.store)
        }
    }
    private func toggleSidebar() {
        #if os(iOS)
        #else
        NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
        #endif
    }


One might also be able to use a package like Swift-Introspection to get a handle on the NSSplitViewController in your SwiftUI code and send the message more directly.

Replies

I also have this issue. I hope there's an answer soon
Even in the Fruta example, if you collapse the sidebar on the Mac by dragging it all the way left, there's no way to get it back. How does one show the toggleSidebar button on macOS using SwiftUI?

Same issue here. I had to delete the Fruta app container to get the sidebar back. This is an issue from the beginning of SwiftUI and should be fixed. We still have to use NSSplitViewController to provide a full functional app.
@martei -- Where did you find the container for Fruta?
Post not yet marked as solved Up vote reply of SGB Down vote reply of SGB
Seems I have the same problem. First both sidebar and detail view were visible. Once the sidebar is collapsed there is no way to bring it back. Any ideas to solve this in code?
@SGB Basically its in you user folder /Library/Containers. Containers folder will be empty in finder. you need to open the Terminal "New Terminal at Folder" command. With a ls in Terminal you will see all containers and can cd a folder to access it.
We are now in development. Can this also happen after we released our app? In my opinion I did something wrong with the NavigationView and frame modifier. After this the sidebar was collapsed. But if this happen also in the release version even the code is correct the users cannot delete the container folder.
Until (one would hope) Apple gets around to it, here's something that'll work.

Using Debug View Hierarchy in the debug toolbar, I found that a private NSSplitViewController is being used for layout. I opted to pass an action down the responder chain to toggle its associated sidebar:

Code Block swift
    private func documentGroup() -> some Scene {
        return DocumentGroup(newDocument: Document()) {
            DocumentView()
                .toolbar {
                    ToolbarItem(placement: .navigation) {
                        Button(action: toggleSidebar, label: {
                            Image(systemName: "sidebar.left")
                        })
                    }
                }
                .environmentObject($0.document.store)
        }
    }
    private func toggleSidebar() {
        #if os(iOS)
        #else
        NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
        #endif
    }


One might also be able to use a package like Swift-Introspection to get a handle on the NSSplitViewController in your SwiftUI code and send the message more directly.

Until Beta 3, the code above worked well. Now, at the bottom of a scrollview, toggling the sidebar causes a massive memory leak.
On beta4, I'm able to add a 'SidebarCommands()' entry to my scene commands. This adds a 'Toggle Sidebar' entry under the View menu on the Mac!

Code Block swift
@SceneBuilder var body: some Scene {
WindowGroup {
... <window views> ...
}
.commands {
SidebarCommands()
}
}

  • Thank you, saved my day.

Add a Comment
What kind of function called of shortcut "⌥⌘+S"? How to call that function to toggle sidebar hide/show function?