swiftuitoolbarswiftui-navigationviewswiftui-tabview

SwiftUI Toolbar In a TabBar View


My first view has a NavigationView with a Tab Bar View

struct TestView: View {
    var body: some View {
        NavigationView {
            TabTestView()
        }
    }
}

In the Tab Bar, I have two views, say TestView1 and TestView2.

struct TabTestView: View {
    var body: some View {
        TabView {
            TestView1()
                .tabItem {
                    Label("Test 1", systemImage: "list.dash")
                }
            TestView2()
                .tabItem {
                    Label("Test 2", systemImage: "plus")
                }
        }
    }
}

I now need to add a toolbar button to TestView1. Here is my attempt to do so:

struct TestView1: View {
var body: some View {
    Text("Hello World")
        .navigationTitle("Test View 1")
        .toolbar {
              ToolbarItem(placement: .navigationBarTrailing) {
                    Button("Save") {
                        print("Toolbar button click")
                    }
                  }
        }
    }
}

While this adds the Navigation Title to the page, the toolbar button is not added, as shown in the screenshot below:

Toolbar button does not show up

If I, however, add the toolbar to the TabTestView, as below, the toolbar button does show up.

struct TabTestView: View {
    var body: some View {
        TabView {
            TestView1()
                .tabItem {
                    Label("Test 1", systemImage: "list.dash")
                }
            TestView2()
                .tabItem {
                    Label("Test 2", systemImage: "plus")
                }
        }
        .toolbar {
              ToolbarItem(placement: .navigationBarTrailing) {
                    Button("Save") {
                        print("Toolbar button click")
                    }
                  }
        }
    }
}

Toolbar Button Shows up

This is a problem - the code to be executed on the button click depends upon variables in TestView1, and adding the toolbar to the TabTestView doesn't feel right, semantically the toolbar "belongs" in TestView1.

Any thoughts would be greatly appreciated.


Solution

  • Update December 20, 2024:

    Apple responded to my feedback, FB9727010. It was a bug, and it is now fixed in iOS 18.2. I verified on the MRE app I sent with the report, and it works for both NavigationView and NavigationStack.

    Once I had working code, I realized I had seen this before. It appears to be a bug in SwiftUI. TabView and NavigationView don't play well together. You will find a lot of my answer will say one NavigationViews at the top of the view hierarchy, which is what you have done. That will not work in this instance. The workaround is to put a NavigationViews in each of the views in the tabs separately for this to work. You will still get all of the correct NavigationViews behavior for each of those hierarchies, but it is duplicative. I don't think I ever filed a Radar on it, but I will today and will post the Radar number here.

    struct TestView: View {
        var body: some View {
            // NavigationView { //Remove this NavigationView
                TabTestView()
            // }
        }
    }
    
    struct TabTestView: View {
        var body: some View {
            TabView {
                TestView1()
                    .tabItem {
                        Label("Test 1", systemImage: "list.dash")
                    }
                TestView2()
                    .tabItem {
                        Label("Test 2", systemImage: "plus")
                    }
            }
        }
    }
    
    struct TestView1: View {
        var body: some View {
            NavigationView { // Add here
                Text("Hello World 1")
                    .navigationTitle("Test View 1")
                    .toolbar {
                        ToolbarItem(placement: .navigationBarTrailing) {
                            Button("Save") {
                                print("Toolbar button click")
                            }
                        }
                    }
            }
        }
    }
    
    
    struct TestView2: View {
        var body: some View {
            NavigationView { // Add here
                Text("Hello World 2")
                    .navigationTitle("Test View 2")
                    .toolbar {
                        ToolbarItem(placement: .navigationBarTrailing) {
                            Button("Save") {
                                print("Toolbar button click")
                            }
                        }
                    }
            }
        }
    }
    

    The Radar number is FB9727010.