Search code examples
iosswiftswiftuiswiftui-navigationstack.searchable

How to show large navigation title when using NavigationStack and .searchable?


I need to always show the search bar, but I want also to start the app with the large title. Instead what I get is a compacted version of it, like in the image: enter image description here

If I scroll then the large title appear, but this looks ugly. And also I don't want to use .navigationBarDrawer(displayMode: .automatic) because it's not intuitive for my specific case. Did anyone found a good workaround for this? I've found some for NavigationView but that don't work for my case with NavigationStack.

Code example:

struct SearchView: View {
    @State var searchText = ""
    @ObservedObject private var viewModel: SearchViewModel 

    var body: some View {
        NavigationStack(path: self.$viewModel.path) {
            List {
                Text("Simple View")
            }
            .navigationBarTitle("Search", displayMode: .large)
            .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always))
        }
    }
}

Solution

  • This seems to be a side effect of having a List as the main content.

    I tried adding various modifiers to the List, to see if a workaround could be found. Sometimes a modifier seemed to help, but not always. For example, adding .containerRelativeFrame(.vertical) works some of the time, but not every time. This suggests, there may be some kind of race condition involved.

    Here is a workaround that seems to work quite reliably on a simulator, at least. The List is added to the screen content in .onAppear.

    @State var isShowing = false
    
    NavigationStack(path: self.$viewModel.path) {
        if isShowing {
            List {
                Text("Simple View")
            }
            .navigationBarTitle("Search", displayMode: .large)
            .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always))
        }
    }
    .onAppear { isShowing = true }
    .onDisappear { isShowing = false }