Hi everyone,
I'm updating my app to adopt the new search bar design in iOS 26 and I'm running into some UI issues depending on the implementation pattern. I'm using Xcode 26.0.1 and SwiftUI 6.
I've tried two main approaches for search, and each has a specific problem related to TabView
.
Pattern 1: Searchable View Inside a Standard Tab
In this pattern, the search bar is specific to one of the main tabs.
The Code:
struct ContentView: View {
var body: some View {
TabView {
Tab("Main", systemImage: "list.bullet") {
MainView()
}
Tab("View1", systemImage: "gearshape") {
Text("View1")
}
Tab("View2", systemImage: "gearshape") {
Text("View2")
}
}
}
}
struct MainView: View {
@State private var searchText = ""
var body: some View {
NavigationStack {
List {
Text("Text 1")
Text("Text 2")
}
.searchable(text: $searchText, placement: .toolbar)
.navigationTitle("Main")
}
}
}
The Problem:
When I preview MainView
directly, the search bar correctly appears at the bottom, matching the new iOS 26 design.
However, when MainView
is presented inside the TabView
in ContentView
, two issues occur:
- Incorrect Position: The search bar reverts to the old style, appearing at the top of the view, attached to the navigation bar.
- Initially Hidden: Often, on the first appearance of the view, the search bar is hidden until I actively pull down on the list.
It seems like the TabView
environment is interfering with the expected placement and initial state of the searchable modifier.
Pattern 2: Dedicated Global Search Tab (role: .search)
Here, I'm using a dedicated tab for a global search experience, with the searchable modifier on the TabView
itself.
The Code:
struct ContentView: View {
@State private var searchText: String = ""
var body: some View {
TabView {
Tab(role: .search) {
SearchView()
}
Tab("Main", systemImage: "list.bullet") {
MainView()
}
Tab("View1", systemImage: "gearshape") {
Text("View1")
}
Tab("View2", systemImage: "gearshape") {
Text("View2")
}
}
.searchable(text: $searchText)
}
}
struct MainView: View {
var body: some View {
NavigationStack {
List {
Text("Text 1")
Text("Text 2")
}
.navigationTitle("Main")
}
}
}
The Problem: The search state is leaking into other tabs in an unexpected way.
Steps to Reproduce:
- Run the app and tap on the "Search" tab.
- Tap the search bar to activate it and bring up the keyboard.
- Now, tap on the "Main" tab.
Result: The app switches to MainView
, but the search bar remains active and focused at the top of the MainView
. This is incorrect; the search UI should be dismissed when switching away from the search context.
Interestingly, if I tap on "View1" or "View2" (which don't have a NavigationStack
), the search bar is correctly dismissed. This suggests the .searchable
modifier on the TabView
is attaching its UI to the first available NavigationStack
it finds in the selected tab.
My Questions:
- For Pattern 1, is there a correct way to ensure the new bottom-placed search bar appears reliably inside a
TabView
? - For Pattern 2, how can I ensure the search state is properly dismissed when navigating away from the search tab, even to a tab that contains a
NavigationStack
?
Is this a potential bug, or am I misusing the APIs for these scenarios? Any guidance or workarounds would be greatly appreciated.
Thanks!
For Pattern 1, is there a correct way to ensure the new bottom-placed search bar appears reliably inside a TabView?
You can place search as a distinct tab on the trailing side of a tab bar use the search role and applying the seachable modifier to that tab. You can also place search in a toolbar either at the bottom or top of the screen. Please review search design conventions in the HIG and Search section in Adopting Liquid Glass.
Result: The app switches to MainView, but the search bar remains active and focused at the top of the MainView. This is incorrect; the search UI should be dismissed when switching away from the search context. Interestingly, if I tap on "View1" or "View2" (which don't have a NavigationStack), the search bar is correctly dismissed. This suggests the .searchable modifier on the TabView is attaching its UI to the first available NavigationStack it finds in the selected tab.
The searchable modifier is contextual, when placed on the TabView, it applies the search field to every navigation stack in every tab. However the state is not shared across the tabs and resets when you change tabs. Instead of applying search to every tab, you may want a single tab be the search tab. For that case, place the searchable modifier in the specific tab to have that tab be the search tab.