Search code examples
swiftuiswiftui-listswiftui-tabview

Why is the search view persisting between tabs?


I have the following code. Initially everything looks alright. However, when I switch to the "Items" tab and then back to the "Rooms" tab, the search view persists. I want it to disappear when "Rooms" is selected.

enter image description here

import SwiftUI

struct HomeView: View {
    
    var body: some View {
        
        NavigationStack {
            TabView {
                Text("Rooms").tabItem {
                    Label("Rooms", systemImage: "pencil")
                }
                
                ItemsSearchView().tabItem {
                    Label("Items", systemImage: "magnifyingglass")
                }
                
            }
        }
    }
}

struct ItemsSearchView: View {
    @State var searchTerm = ""
    @State var listData = [Item](arrayLiteral: Item(id: "abc", name: "first"), Item(id: "def", name: "second"))
    
    var body: some View {
        VStack {
            List(listData) { curItem in
                Text(curItem.name)
            }
            .searchable(text: $searchTerm)
        }
    }
}

public struct Item: Identifiable {
    public var id : String
    public var name: String
}

struct HomeView_Previews: PreviewProvider {
    static var previews: some View {
        HomeView()
    }
}



Solution

  • Your TabView exists inside a NavigationStack. The searchable modifier attaches its search field to the navigation bar, which exists further up the view hierarchy than the tab view. So, when you switch tabs, the navigation bar does not change and the search field remains.

    You should move TabView to be the primary view. Each tab should then have its own NavigationStack. For example:

    struct HomeView: View {
      var body: some View {
        TabView {
          RoomsView().tabItem {
            Label("Rooms", systemImage: "pencil")
          }
          ItemsSearchView().tabItem {
            Label("Items", systemImage: "magnifyingglass")
          }
        }
      }
    }
    
    struct ItemsSearchView: View {
      @State var searchTerm = ""
      // etc.
    
      var body: some View {
        NavigationStack {
          List(listData) { item in 
            // etc.
          }
          .searchable(text: $searchTerm)
        }
      }
    }
    

    Generally, Apple's navigation components all expect navigation stacks to exist independently within each tab, so that the buttons on the tab bar are available to take users to different sections of your app at any time.

    Apple's best practices for tab bars used to be more explicit about this, I'm sure, but I think the best summation of why the tab view should be at the top of the hierarchy is still there:

    Make sure the tab bar is visible when people navigate to different areas in your app. The exception is a tab bar within a modal view. Because a modal view gives people a separate experience that they dismiss when they’re finished, hiding the view’s tab bar doesn’t affect app navigation.