iosswiftswiftuitabbar

How to hide TabBar while keeping tab navigation functional in SwiftUI?


I have a SwiftUI setup where I'm using a TabView for navigation between different views. In larger screen sizes (width > 900), I've implemented a side menu using an HStack to provide a more convenient way of switching tabs. However, in this setup, I want to hide the tab bar that's normally used for navigation between tabs, while still keeping the tab navigation functional.

Here's a simplified version of my code:

    
    // Other code...
    
    var body: some View {
        GeometryReader { geometry in
            HStack(spacing: 0) {
                if geometry.size.width > 900 {
                    // Side menu implementation...
                }
                TabMenuView(activeTab: $route)
                    .frame(maxWidth: .infinity)
                    .toolbar(.hidden, for: .tabBar) // This only works, outside the root of a nvaigation stack
            }
        }
    }
}

struct TabMenuView: View {
    
    // Other code...
    
    var body: some View {
        TabView(selection: $activeTab) {
            // Tab items...
        }
    }
}

I tried a lot, but I just can't hide the TabBar, without hiding the whole TabView. It work's, when a view is pushed to a NavigationStack with the .toolbar(.hidden, for: .tabBar), but thats not the behavior I am hoping for.


Solution

  • You should maintain the selected tab and use bindings where both UI versions depend on, respectively set it:

    
    import SwiftUI
    
    struct MainView: View {
        
        enum Tabs: String {
            case one
            case two
        }
        
        // @Environment(\.horizontalSizeClass) var horizontalSizeClass
        
        @State private var tabSelection: Tabs = .two
    
        var body: some View {
            GeometryReader { geometry in
                if geometry.size.width <= 900 {
                    TabView(selection: $tabSelection) {
                        ContentView()
                        .tabItem {
                            Label(Tabs.one.rawValue, systemImage: "1.square")
                        }
                        .tag(Tabs.one.rawValue)
    
                        OtherView()
                        .tabItem {
                            Label(Tabs.two.rawValue, systemImage: "2.square")
                        }
                        .tag(Tabs.two.rawValue)
                    }
                } else {
                    // use Custom Menu
                    VStack {
                        switch tabSelection {
                        case .one:
                            HStack {
                                Button("Show Tab 2") {
                                    tabSelection = .two
                                }
                                Spacer()
                                ContentView()
                            }
                        case .two:
                            HStack {
                                Button("Show Tab 1") {
                                    tabSelection = .one
                                }
                                Spacer()
                                OtherView()
                            }
                        }
                    }
                }
    
            }
        }
        
    }
    
    struct ContentView: View {
        var body: some View {
            VStack {
                Image(systemName: "globe")
                    .imageScale(.large)
                    .foregroundStyle(.tint)
                Text("Hello, world!")
            }
            .padding()
        }
    }
    
    struct OtherView: View {
        var body: some View {
            VStack {
                Text("Other view")
            }
            .padding()
        }
    }
    
    
    #Preview {
        MainView()
    }
    

    Note: you may use Size Classes to determine the layout and UI.