5

I'm trying to hide my TabView when I push a new view in my NavigationView but for now it seems that there is no way to do it (I saw a lot of thing on Internet, but nothing seems work properly for me)?


By default my code look like this:

struct ContentView: View {
    var body: some View {
        TabView {
            NavigationView {
                view1
            }
            .tabItem {
                Image(systemName: "house.fill")
                Text("Home")
            }
            
            NavigationView {
                view2
            }
            .tabItem {
                Image(systemName: "bookmark.circle.fill")
                Text("Bookmark")
            }
        }
        .accentColor(.red)
    }
    
    private var view1: some View {
        List {
            NavigationLink {
                DetailsView()
            } label: {
                Text("View 1")
            }
        }
        .navigationTitle("View 1")
    }
    
    private var view2: some View {
        List {
            NavigationLink {
                DetailsView()
            } label: {
                Text("View 2")
            }
        }
        .navigationTitle("View 2")
    }
}

struct DetailsView: View {
    var body: some View {
        EmptyView()
    }
}

But then, I don't have any solution to hide my TabView, so I try something like this in my ContentView:

var body: some View {
    NavigationView {
        TabView {
            view1
                .tabItem {
                    Image(systemName: "house.fill")
                    Text("Home")
                }
                .navigationTitle(Text("title"))
                
            view2
                .tabItem {
                    Image(systemName: "bookmark.circle.fill")
                    Text("Bookmark")
                }
                .navigationTitle(Text("title"))
        }
        .accentColor(.red)
    }
}

Now, the hide on push is working, but it cause some glitch in the navigation bar and I can't set multiple navigationTitle (one for each view) like I did before; I can set only one navigationTitle for the NavigationView.

To solve the NavigationView title, I found a workaround by using a @State variable, but it remains this glitch on the navigation bar: sometimes the navigation view background is working, sometimes it's not working and sometimes I have a spacing between the title and the content (like in the Bookmark tab):

The navigation bar glitch

What am I doing wrong?

Does it exist any solution for this issue (hidesBottomBarWhenPushed + navigation bar glitch)?

Aniruddh Parihar
  • 3,072
  • 3
  • 21
  • 39

2 Answers2

1

A possible approach is to place NavigationView and TabView in ZStack (first above second) and use both separately, synchronising them with selection.

The only challenge here is to calculate height of top view to make tab bar visible, as soon as we achieve this the tab bar visibility can be just on/off depending on link activation.

Tested with Xcode 14 / iOS 16

demo

Main part:

    ZStack {
        TabView(selection: $selection) {
            GeometryReader { gp in
                Color.clear.onAppear {
                        height = gp.size.height
                    }.onChange(of: gp.size) { _ in
                        height = gp.size.height
                    }
            }
            .tabItem {
                Image(systemName: "house.fill")
                Text("Home")
            }.tag(1)
            Color.clear
            .tabItem {
                Image(systemName: "bookmark.circle.fill")
                Text("Bookmark")
            }.tag(2)
        }
        .accentColor(.red)

        NavigationView {
            if selection == 1 {
                view1
            } else {
                view2
            }
        }
        .frame(maxHeight: tabBarVisible ? height : .infinity)
        .frame(maxHeight: .infinity, alignment: .top)
        .onChange(of: linkedTag) { tag in
            tabBarVisible = tag == nil
        }

Test code on GitHub

Asperi
  • 228,894
  • 20
  • 464
  • 690
  • 1
    Hello @Asperi. Yes, your solution is working, but there are two issue that are not good for me : 1. the `TabView` appear and disappear without animation (not very beautiful) 2. when you come back and the `TabView` appears, it appears with a delay. I'm trying to have the same effect that in UIKit (if possible), or if not, at least something that looks good for a premium application. – Alexandre Cools Aug 02 '22 at 08:02
  • Did you find any solution on this? – adri567 Jan 26 '23 at 19:09
1

We're able to achieve this behavior if we make NavigationStack (available from iOS 16) as the root view and place TabView inside it. Unfortunately we will have only one NavigationStack for each tabs (personally I don't like this approach because I think each Tab should have a personal NavigationStack/NavigationView). I add an example code and share a simple project link.

TabViewExampleApp code

@main
struct TabViewExampleApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

ContentView code

struct ContentView: View {
    @State private var selectedTab: TabType = .chats

    var body: some View {
        NavigationStack {
            TabView(selection: $selectedTab) {
                ForEach(Array(TabType.allCases)) { tab in
                    SimpleView(title: tab.title)
                        .tabItem {
                            Label(tab.title, systemImage: tab.systemImage)
                        }
                        .tag(tab)
                }
            }
            .navigationTitle(selectedTab.title)
        }
    }
}

TabType code

enum TabType: Int, Hashable, CaseIterable, Identifiable {
    case contacts
    case calls
    case chats
    case settings

    var id: Int {
        rawValue
    }

    var title: String {
        String(describing: self)
            .capitalized
    }
    var systemImage: String {
        switch self {
        case .contacts:
            return "folder.fill"
        case .calls:
            return "arrow.right.doc.on.clipboard"
        case .chats:
            return "paperplane.circle.fill"
        case .settings:
            return "gear"
        }
    }
}

SimpleView code

struct SimpleView: View {
    let title: String

    var body: some View {
        VStack {
            NavigationLink {
                DetailView()
            } label: {
                Text(title)
            }
        }
    }
}

DetailView code

struct DetailView: View {
    var body: some View {
        Text("Hello, World!")
            .foregroundColor(.green)
            .background(.blue)
    }
}

Simple example project is on GitHub.

Seems it doesn't work out of the box due Apple wants that tabs should be always visible. Make sure the tab bar is visible when people navigate to different areas in your app - from Human Interface Guidelines

Demo:

enter image description here

Alexander Khitev
  • 6,417
  • 13
  • 59
  • 115