Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 tried adding a boolean state variable that controls whether the sidebar should show or not, but that doesn't work because then the main view is turned translucent (I guess this is because macOS thinks the sidebar is now the main view).

Is there a way to natively achieve this in SwiftUI? Please note I'm using macOS Big Sur, Xcode 12, and SwiftUI 2.0

Thanks in advance.

My code

ContentView.swift

import SwiftUI

struct ContentView: View {

    #if os(iOS)
    @Environment(\.horizontalSizeClass) private var horizontalSizeClass
    #endif
    
    @ViewBuilder var body: some View {
        #if os(iOS)
        if horizontalSizeClass == .compact {
            TabController()
        } else {
            SidebarNavigation()
        }
        #else
        SidebarNavigation()
            .frame(minWidth: 900, maxWidth: .infinity, minHeight: 500, maxHeight: .infinity)
        #endif
    }
    
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .previewLayout(.sizeThatFits)
    }
}

Sidebar.swift

import SwiftUI

struct SidebarNavigation: View {
    
    enum HyperspaceViews {
        case home
        case localTimeline
        case publicTimeline
        case messages
        case announcements
        case community
        case recommended
        case profile
    }

    @State var selection: Set<HyperspaceViews> = [.home]
    @State var searchText: String = ""
    @State var showComposeTootView: Bool = false
    @State var showNotifications: Bool = false
    @State private var showCancelButton: Bool = false
    
    
    var sidebar: some View {
        
        VStack {
            
            HStack {
                TextField("Search...", text: $searchText)
                    .cornerRadius(4)
            }
                .padding()
                .textFieldStyle(RoundedBorderTextFieldStyle())
            
            List(selection: self.$selection) {
                    
                Group {
                    
                    NavigationLink(destination: Timeline().container.frame(maxWidth: .infinity, maxHeight: .infinity)) {
                        Label("Home", systemImage: "house")
                    }
                        .accessibility(label: Text("Home"))
                        .tag(HyperspaceViews.home)
                    
                    NavigationLink(destination: Text("Local").frame(maxWidth: .infinity, maxHeight: .infinity)) {
                        Label("Local", systemImage: "person.2")
                    }
                        .accessibility(label: Text("Local"))
                        .tag(HyperspaceViews.localTimeline)
                    
                    NavigationLink(destination: Text("Public").frame(maxWidth: .infinity, maxHeight: .infinity)) {
                        Label("Public", systemImage: "globe")
                    }
                        .accessibility(label: Text("Public"))
                        .tag(HyperspaceViews.localTimeline)
                    
                    NavigationLink(destination: Text("Messages").frame(maxWidth: .infinity, maxHeight: .infinity)) {
                        Label("Messages", systemImage: "bubble.right")
                    }
                        .accessibility(label: Text("Message"))
                        .tag(HyperspaceViews.localTimeline)
                    
                    Divider()
                        
                    NavigationLink(destination: Text("Announcements").frame(maxWidth: .infinity, maxHeight: .infinity)) {
                        Label("Announcements", systemImage: "megaphone")
                    }
                        .accessibility(label: Text("Announcements"))
                        .tag(HyperspaceViews.announcements)
                    
                    NavigationLink(destination: Text("Community").frame(maxWidth: .infinity, maxHeight: .infinity)) {
                        Label("Community", systemImage: "flame")
                    }
                        .accessibility(label: Text("Community"))
                        .tag(HyperspaceViews.community)
                    
                    NavigationLink(destination: Text("Recommended").frame(maxWidth: .infinity, maxHeight: .infinity)) {
                        Label("Recommended", systemImage: "star")
                    }
                        .accessibility(label: Text("Community"))
                        .tag(HyperspaceViews.recommended)
                    
                    Divider()
                    NavigationLink(destination: Text("Recommended").frame(maxWidth: .infinity, maxHeight: .infinity)) {
                        Label("hyperspacedev", systemImage: "tag")
                    }
                        .accessibility(label: Text("Community"))
                        .tag(HyperspaceViews.recommended)
                        
                }
                
            }
                .overlay(self.profileButton, alignment: .bottom)
            .listStyle(SidebarListStyle())
            
        }
    }
    
    var profileButton: some View {
        VStack(alignment: .leading, spacing: 0) {
            Divider()
            NavigationLink(destination: ProfileView().container.frame(maxWidth: .infinity, maxHeight: .infinity)) {
                HStack {
                    Image("amodrono")
                        .resizable()
                        .clipShape(Circle())
                        .frame(width: 25, height: 25)
                    Text("amodrono")
                        .font(.headline)
                }
                .contentShape(Rectangle())
            }
            .accessibility(label: Text("Your profile"))
            .padding(.vertical, 8)
            .padding(.horizontal, 16)
            .buttonStyle(PlainButtonStyle())
        }
            .tag(HyperspaceViews.profile)
    }
    
    var body: some View {
        NavigationView {
            
            #if os(macOS)
            sidebar.frame(minWidth: 100, idealWidth: 180, maxWidth: 200, maxHeight: .infinity)
            #else
            sidebar
            #endif
            
            Text("Content List")
                .frame(maxWidth: .infinity, maxHeight: .infinity)
            
        }
            .sheet(isPresented: self.$showComposeTootView) {
                ComposeTootView(showComposeTootView: self.$showComposeTootView)
                    .frame(minWidth: 400, maxWidth: .infinity, minHeight: 200, maxHeight: .infinity)
            }

            .toolbar {
                
                ToolbarItem {
                    
                    Button(action: {
                        
                        self.showNotifications.toggle()
                        
                    }) {
                        
                        Image(systemName: "bell")
                        
                    }
                        .popover(
                            isPresented: self.$showNotifications,
                            arrowEdge: .bottom
                        ) {
                            LazyVStack {
                                ForEach(0 ..< 10 ) { i in
                                    
                                    Label("@\(i) liked your post!", systemImage: "hand.thumbsup")
                                        .padding()
                                    
                                    Divider()
                                    
                                }
                            }
                        }
                    
                }
                
                ToolbarItem {
                    
                    Button(action: {
                        
                        self.showComposeTootView.toggle()
                        
                    }) {
                        
                        Image(systemName: "square.and.pencil")
                        
                    }
                }
                
             }
    }
    
}

struct SidebarNavigation_Previews: PreviewProvider {
    static var previews: some View {
        SidebarNavigation()
    }
}
like image 893
iAlex11 Avatar asked Jun 25 '20 14:06

iAlex11


Video Answer


2 Answers

This worked for me - https://developer.apple.com/forums/thread/651807

struct SwiftUIView: View {
    var body: some View {
        NavigationView{

        }.toolbar {
            ToolbarItem(placement: .navigation) {
                Button(action: toggleSidebar, label: {
                    Image(systemName: "sidebar.left")
                })
            }
        }
    }
}

func toggleSidebar() {
        NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
}

struct SwiftUIView_Previews: PreviewProvider {
    static var previews: some View {
        SwiftUIView()
    }
}

It shows in the views on iOS so you will need some conditions for macOS only.

like image 150
fabpabs Avatar answered Oct 02 '22 04:10

fabpabs


Apple provide NSToolbarToggleSidebarItemIdentifier which perform the toggleSidebar(_:) method.

.onAppear {
    NSApp.keyWindow?.toolbar?.insertItem(withItemIdentifier: .toggleSidebar, at: 0)
}

This way will appear a button in your toolbar which action will invoke the sidebar toggle and it works perfectly.

like image 28
AlbertUI Avatar answered Oct 02 '22 03:10

AlbertUI