SwiftUI Toolbar… bug?

Hi everyone! I recently noticed a strange behaviour with the new SwiftUI Toolbar.

I have an app with only 2 views: a ContentView and a DetailView. My goal is to have a different toolbar in both.

ContentView:
Code Block
struct ContentView: View {
    @State var isFavorite = false
    var body: some View {
        NavigationView {
            VStack {
                Image(systemName: isFavorite ? "star.fill" : "star")
                NavigationLink(destination: DetailView(isFavorite: $isFavorite)) {
                    Text("Go")
                }
            }
            .navigationTitle("ContentView")
            .toolbar() {
                ToolbarItem(placement: .bottomBar) {
                    Text("This is my ContentView")
                }
            }
        }
    }
}

DetailView:
Code Block
struct DetailView: View {
    @Binding var isFavorite: Bool
    var body: some View {
        Button("Tap me!") {
            isFavorite.toggle()
        }
        .navigationTitle("DetailView")
        .toolbar() {
            ToolbarItem(placement: .bottomBar) {
                Text("This is my DetailView")
            }
            ToolbarItem(placement: .navigationBarTrailing) {
                Image(systemName: isFavorite ? "star.fill" : "star")
            }
        }
    }
}

When I toggle the @Binding property in the DetailView, the toolbar is replaced by another empty toolbar appearing from nowhere.

Am I doing something wrong?

Replies

I'm also facing the same issue: no clue how to fix this...
I don't know if it's a bug or just undefined behavior. It looks like it always displays a blank toolbar when isFavorite gets updated. My guess is that SwiftUI don't know which one should be on top (or it renders the ContentView toolbar, but it doesn't show the text because it doesn't exist in DetailView?). If you comment out the Image(systemName: isFavorite ? "star.fill" : "star"), the ContentView doesn't get rendered again, so it doesn't change the toolbar, but it seems that when DetailView propagates an update to ContentView is clashes over what ToolbarItem(placement: .bottomBar) to use.

I tried a couple different solutions, conditional in the ToolbarItem, making an observable object, settings views with onAppear and onDisappear, but non of them resulted in a work around for this problem.

Either it it s bug, or this type of behavior isn't supposed to happen in SwiftUI. Do you have an example of an App that is dynamically changing the toolbar based on the view in SwiftUI?
Has anybody solved this yet? Any help would be appreciative.
I resolved this problem by explicitly setting the navigationViewStyle to StackNavigationViewStyle.

Code Block
var body: some View {       
NavigationView {
/* ... */
}
    .navigationViewStyle(StackNavigationViewStyle())
}

  • My app was working fine UNTIL I added in .navigationViewStyle(StackNavigationViewStyle()). Now, after I added it in, to make the iPad version work the way I want it to, it has the problem described by OP.

    Any other ideas?

Add a Comment

I'm still seeing the same issue on Xcode Version 13.3.1 (13E500a)... adding the StackNavigationStyle() modifier fixed it, but it's really a hack.

Any updates about this from the devs?

Such a weird bug. Still happening two years later with iOS 16.4.

struct ContentView: View {
    var body: some View {
        NavigationView {
            List {
                NavigationLink("Go to detail view") {
                    Text("Detail view")
                        .toolbar {
                            ToolbarItem(placement: .bottomBar) {
                                Button(action: { print("tap") }) {
                                    Image(systemName: "square.and.arrow.up")
                                }
                            }
                        }
                }
            }
        }
        // Without this, toolbar is either empty or doesn't appear in detail view
        .navigationViewStyle(.stack)
    }
}

After wasting hours with this weird behavior, I've accepted that this is a perfect speciment of the "you're holding it wrong"-situation.

Let me put it this way (thinking about this a few times solved my problem):

Whatever you want to render above/below the List (which is why you need the scrollview) can also be put into a separate Segment of the list. It won't look as bad as you might initially think!