swiftswiftuiswiftui-tabviewswiftui-toolbar

How to hide navigation toolbar in SwiftUI using a tabview setup


When I initially run the application, I have the add button present on the first tab, on the second tab, the add button shouldnt be visible but when I tab back to the first tab, the button is gone. Here is my code for ListView and ContentView. I am trying to figure out a way to use the tags setup to hide the toolbar add button that would automatically update the UI.


struct TaskListView: View {
    
    @ObservedObject var taskStore: TaskStore
    @State private var selectedTab: Tab = .tasks
    
    enum Tab {
        case tasks
        case completed
    }
    
    var body: some View {
        TabView {
            List(taskStore.tasks.filter { !$0.isCompleted }, id: \.self) { task in
                NavigationLink(value: task) {
                    VStack {
                        TaskRowView(task: task)
                    }
                    .padding([.leading, .trailing], 20)
                }
                .navigationDestination(for: Task.self) { task in
                    TaskDetailView(task: $taskStore.tasks
                        .first(where: { $0.id == task.id })!)
                }
            }
            .tabItem {
                Image(systemName: "list.bullet.circle")
                Text("Tasks")
                }
            .tag(Tab.tasks)
            .toolbar(.visible, for: .navigationBar)
            List(taskStore.tasks.filter { $0.isCompleted }, id: \.self) { task in
                NavigationLink(value: task) {
                    VStack {
                        TaskRowView(task: task)
                        }
                    .padding([.leading, .trailing], 20)
                    }
                .navigationDestination(for: Task.self) { task in
                    TaskDetailView(task: $taskStore.tasks
                        .first(where: { $0.id == task.id })!)
                }
            }
            .tabItem {
                Image(systemName: "checkmark.circle")
                Text("Completed")
                }
            .tag(Tab.completed)
            .toolbar(.hidden, for: .navigationBar)
          }
          
        }
    }
    
    struct TaskListView_Previews: PreviewProvider {
        static var previews: some View {
            TaskListView(taskStore: TaskStore())
        }
    }

Contentview file:

import SwiftUI

struct ContentView: View {
  @StateObject var taskStore = TaskStore()
  
  var body: some View {
    NavigationStack {
      VStack {
        if taskStore.tasks.isEmpty {
          Text("No tasks found")
        } else {
          TaskListView(taskStore: taskStore)
        }
        Spacer()
        //NewTaskButton(addingTask: $addingTask)
      }
      .navigationTitle("My Tasks")
      .toolbar {
        ToolbarItem(placement: .navigationBarTrailing) {
    
                NewTaskButtonView(taskStore: taskStore)
            
         
        }
      }
    }
  }
  
}

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

Solution

  • The TabBar should always be on top of the view hierarchy.

                        TabView
                       /       \
                Navigation   Navigation
                    |            |
                   List1        List2
    

    Following your requirements, I would prefer to refactor your code as below.

    Firstly, break into two lists, IncompleteTaskView and CompletedTaskView. You're using taskStore for displaying purpose only so I will use normal variable. You may need to change to @Enviroment object later if needed.

    struct IncompleteTaskView: View {
      var taskStore: TaskStore
    
      var body: some View {
        NavigationStack {
          VStack {
            ...
          }
          .navigationTitle("Incomplete Tasks")
          .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
              NewTaskButtonView(taskStore: taskStore)
            }
          }
        }
      }
    }
    

    The same implementation with CompletedTaskView, except .toolBar.


    Secondly, the ContentView will have TabView only:

    struct ContentView: View {
      ...
      @StateObject private var taskStore = TaskStore()
      
      var body: some View {
        TabView(selection: $selectedTab) {
          IncompleteTaskView(taskStore: taskStore)
            .tabItem {
                Image(systemName: "list.bullet.circle")
                Text("Tasks")
            }
            .tag(Tab.tasks)
    
          CompletedTaskView(taskStore: taskStore)
            .tabItem {
                Image(systemName: "checkmark.circle")
                Text("Completed")
            }
            .tag(Tab.completed)
          }
        }
      }
    }