SwiftUI Blog

Mastering SwiftUI: Your Guide to Building Beautiful, Intuitive Apps.

TabView and Search

With iOS 26, Apple introduced a new position for the search bar. It can now appear either in the top toolbar or at the bottom, to the right of the TabView, as shown in the figure:

It’s easy to add, we just need to add a tab with the search role:

import SwiftUI

struct ContentView: View {
    @State var nomi = ["superman", "batman", "wonderwoman"]
    @State var searchText: String = ""
    @State var searchPresented = false
    
    var body: some View {
        TabView() {
            Tab("First", systemImage: "car") {
                Text("In the first tab")
            }
            Tab("Second", systemImage: "bicycle") {
                Text("In the second tab")
            }
            Tab(role: .search) {
                NavigationStack {
                    List(searchResult(text: searchText), id: \.self) { nome in
                        NavigationLink(destination: EmptyView()) {
                            Text(nome)
                        }
                    }
                }
            }
        }.searchable(text: $searchText)
    }
    func searchResult(text: String) -> [String] {
        if text.count == 0 {
            return nomi
        }
        return nomi.filter({
            (nome: String) -> Bool in
                return nome.lowercased().contains(text.lowercased())
        })
    }
}

Note that the searchable modifier is attached to the TabView. The logic is the same as for a search bar in the navigation bar.

Also, I’d like to point out something: if you have a pre-selected tab, make sure it’s set to the search tab. Otherwise, you’ll run into an issue where you can’t insert text into the search field. I’m not sure whether this is a bug or not, but it can be quite frustrating.

A working example of this behavior is:

struct ContentView: View {
    @State var nomi = ["superman", "batman", "wonderwoman"]
    @State var searchText: String = ""
    @State var searchPresented = false
    
    var body: some View {
        TabView(selection: .constant(2)) {
            Tab("First", systemImage: "car", value: 0) {
                Text("In the first tab")
            }
            Tab("Second", systemImage: "bicycle", value: 1) {
                Text("In the second tab")
            }
            Tab(value: 2, role: .search) {
                NavigationStack {
                    List(searchResult(text: searchText), id: \.self) { nome in
                        NavigationLink(destination: EmptyView()) {
                            Text(nome)
                        }
                    }
                }
            }
        }.searchable(text: $searchText)
    }
    func searchResult(text: String) -> [String] {
        if text.count == 0 {
            return nomi
        }
        return nomi.filter({
            (nome: String) -> Bool in
                return nome.lowercased().contains(text.lowercased())
        })