Popout Sidebar Menu in SwiftUI

Today I decided to mess around with creating a SwiftUI View which allows me to easily create a sidebar menu for content inside my apps. After designing a few prototypes, I generalized it into a single custom view class called SideBarStack as shown below.

If you found this tutorial helpful, please consider subscribing using this link, and if you aren't reading this on TrailingClosure.com, please come check us out sometime!

struct SideBarStack<SidebarContent: View, Content: View>: View {
    
    let sidebarContent: SidebarContent
    let mainContent: Content
    let sidebarWidth: CGFloat
    @Binding var showSidebar: Bool
    
    init(sidebarWidth: CGFloat, showSidebar: Binding<Bool>, @ViewBuilder sidebar: ()->SidebarContent, @ViewBuilder content: ()->Content) {
        self.sidebarWidth = sidebarWidth
        self._showSidebar = showSidebar
        sidebarContent = sidebar()
        mainContent = content()
    }
    
    var body: some View {
        ZStack(alignment: .leading) {
            sidebarContent
                .frame(width: sidebarWidth, alignment: .center)
                .offset(x: showSidebar ? 0 : -1 * sidebarWidth, y: 0)
                .animation(Animation.easeInOut.speed(2))
            mainContent
                .overlay(
                    Group {
                        if showSidebar {
                            Color.white
                                .opacity(showSidebar ? 0.01 : 0)
                                .onTapGesture {
                                    self.showSidebar = false
                                }
                        } else {
                            Color.clear
                            .opacity(showSidebar ? 0 : 0)
                            .onTapGesture {
                                self.showSidebar = false
                            }
                        }
                    }
                )
                .offset(x: showSidebar ? sidebarWidth : 0, y: 0)
                .animation(Animation.easeInOut.speed(2))
                
        }
    }
}

As you can see, the custom view utilizes the @ViewBuilder Property Wrapper twice to allow you to pass in custom content for both the Sidebar and actual view content.

The custom SideBarStack also takes in two parameters: sidebarWidth and showSidebar. The first allows the view to translate both the sidebar and main content correctly when the sidebar opens and closes. It's as simple as setting the offset of the two views according to the passed in width.

Second the showSidebar bool controls whether or not the sidebar is open. It's passed in as a @Binding to allow a  TapGesture  be placed on the main content. This allows a user to tap the main content to close the sidebar and not just the menu button. In the code, this works by applying an almost completely clear overlay. SwiftUI will not trigger the tap gesture on a Color.clear view or a view whose opacity is 0.

Example Use

struct ContentView: View {
    
    // Controls display of sidebar
    @State var showSidebar: Bool = false
    
    var body: some View {
        SideBarStack(sidebarWidth: 125, showSidebar: $showSidebar) {
           // Sidebar content here
        } content: {
           // Main content here
        }.edgesIgnoringSafeArea(.all)
    }
}

Like this tutorial?

Show us what you've made!

Send us pics! Drop us a link! Anything! Find us on Twitter @TrailingClosure, on Instagram, or email us at [email protected].