SwiftUI Navigation API Reference Overview
SwiftUI's navigation APIs provide data-driven, programmatic navigation that scales from simple stacks to complex multi-column layouts. Introduced in iOS 16 (2022) with NavigationStack and NavigationSplitView, evolved in iOS 18 (2024) with Tab/Sidebar unification, and refined in iOS 26 (2025) with Liquid Glass design.
Evolution timeline 2022 (iOS 16) NavigationStack, NavigationSplitView, NavigationPath, value-based NavigationLink 2024 (iOS 18) Tab/Sidebar unification, sidebarAdaptable style, zoom navigation transition 2025 (iOS 26) Liquid Glass navigation chrome, bottom-aligned search, floating tab bars, backgroundExtensionEffect Key capabilities Data-driven navigation NavigationPath represents stack state, enabling programmatic push/pop and deep linking Multi-column layouts NavigationSplitView adapts automatically (3-column on iPad → single stack on iPhone) State restoration Codable NavigationPath + SceneStorage for persistence across app launches Tab integration Per-tab NavigationStack with state preservation on tab switch (iOS 18+) Liquid Glass Automatic glass navigation bars, sidebars, and toolbars (iOS 26+) When to use vs UIKit SwiftUI navigation New apps, multiplatform, simpler navigation flows → Use NavigationStack/SplitView UINavigationController Complex coordinator patterns, legacy code, specific UIKit features → Consider UIKit Related Skills Use axiom-swiftui-nav for anti-patterns, decision trees, pressure scenarios Use axiom-swiftui-nav-diag for systematic troubleshooting of navigation issues When to Use This Skill
Use this skill when:
Learning navigation APIs from NavigationStack to NavigationSplitView to NavigationPath Implementing WWDC examples (all 4 sessions with code examples included) Planning deep linking with URL routing and NavigationPath manipulation Setting up state restoration with Codable NavigationPath and SceneStorage Adopting iOS 26+ features Liquid Glass navigation, bottom-aligned search, tab bar minimization Choosing navigation architecture Stack vs SplitView vs Tab+Navigation patterns Implementing coordinator/router patterns alongside SwiftUI's built-in navigation API Evolution Timeline Year iOS Version Key Features 2020 iOS 14 NavigationView (deprecated iOS 16) 2022 iOS 16 NavigationStack, NavigationSplitView, NavigationPath, value-based NavigationLink 2024 iOS 18 Tab/Sidebar unification, sidebarAdaptable, TabSection, zoom transitions 2025 iOS 26 Liquid Glass navigation, backgroundExtensionEffect, tabBarMinimizeBehavior NavigationView (Deprecated) vs NavigationStack/SplitView Feature NavigationView (iOS 13-15) NavigationStack/SplitView (iOS 16+) Programmatic navigation Per-link isActive bindings Single NavigationPath for entire stack Deep linking Complex, error-prone Simple path manipulation Type safety View-based, runtime checks Value-based, compile-time checks State restoration Manual, difficult Built-in Codable support Multi-column NavigationStyle enum Dedicated NavigationSplitView Status Deprecated iOS 16 Current API Recommendation New apps: Use NavigationStack and NavigationSplitView exclusively Existing apps: Migrate from NavigationView (deprecated) See "Migrating to new navigation types" documentation NavigationStack Complete Reference
NavigationStack represents a push-pop interface like Settings on iPhone or System Settings on macOS.
1.1 Creating NavigationStack Basic NavigationStack NavigationStack { List(Category.allCases) { category in NavigationLink(category.name, value: category) } .navigationTitle("Categories") .navigationDestination(for: Category.self) { category in CategoryDetail(category: category) } }
With Path Binding (WWDC 2022, 6:05) struct PushableStack: View { @State private var path: [Recipe] = [] @StateObject private var dataModel = DataModel()
var body: some View {
NavigationStack(path: $path) {
List(Category.allCases) { category in
Section(category.localizedName) {
ForEach(dataModel.recipes(in: category)) { recipe in
NavigationLink(recipe.name, value: recipe)
}
}
}
.navigationTitle("Categories")
.navigationDestination(for: Recipe.self) { recipe in
RecipeDetail(recipe: recipe)
}
}
.environmentObject(dataModel)
}
}
Key points:
path: $path binds the navigation state to a collection Value-presenting NavigationLink appends values to the path navigationDestination(for:) maps values to views 1.2 NavigationLink (Value-Based) Value-presenting NavigationLink // Correct: Value-based (iOS 16+) NavigationLink(recipe.name, value: recipe)
// Correct: With custom label NavigationLink(value: recipe) { RecipeTile(recipe: recipe) }
// Deprecated: View-based (iOS 13-15) NavigationLink(recipe.name) { RecipeDetail(recipe: recipe) // Don't use in new code }
How NavigationLink works with NavigationStack NavigationStack maintains a path collection Tapping a value-presenting link appends the value to the path NavigationStack maps navigationDestination modifiers over path values Views are pushed onto the stack based on destination mappings 1.3 navigationDestination Modifier Single Type .navigationDestination(for: Recipe.self) { recipe in RecipeDetail(recipe: recipe) }
Multiple Types NavigationStack(path: $path) { RootView() .navigationDestination(for: Recipe.self) { recipe in RecipeDetail(recipe: recipe) } .navigationDestination(for: Category.self) { category in CategoryList(category: category) } .navigationDestination(for: Chef.self) { chef in ChefProfile(chef: chef) } }
Placement rules Place navigationDestination outside lazy containers (not inside ForEach) Place near related NavigationLinks for code organization Must be inside NavigationStack hierarchy // Correct: Outside lazy container ScrollView { LazyVGrid(columns: columns) { ForEach(recipes) { recipe in NavigationLink(value: recipe) { RecipeTile(recipe: recipe) } } } } .navigationDestination(for: Recipe.self) { recipe in RecipeDetail(recipe: recipe) }
// Wrong: Inside ForEach (may not be loaded) ForEach(recipes) { recipe in NavigationLink(value: recipe) { RecipeTile(recipe: recipe) } .navigationDestination(for: Recipe.self) { r in // Don't do this RecipeDetail(recipe: r) } }
1.4 NavigationPath
NavigationPath is a type-erased collection for heterogeneous navigation stacks.
Typed Array vs NavigationPath // Typed array: All values same type @State private var path: [Recipe] = []
// NavigationPath: Mixed types @State private var path = NavigationPath()
NavigationPath Operations // Append value path.append(recipe)
// Pop to previous path.removeLast()
// Pop to root path.removeLast(path.count) // or path = NavigationPath()
// Check count if path.count > 0 { ... }
// Deep link: Set multiple values path.append(category) path.append(recipe)
Codable Support // NavigationPath is Codable when all values are Codable @State private var path = NavigationPath()
// Encode let data = try JSONEncoder().encode(path.codable)
// Decode let codableRep = try JSONDecoder().decode(NavigationPath.CodableRepresentation.self, from: data) path = NavigationPath(codableRep)
NavigationSplitView Complete Reference
NavigationSplitView creates multi-column layouts that adapt to device size.
2.1 Two-Column Layout Basic Two-Column (WWDC 2022, 10:40) struct MultipleColumns: View { @State private var selectedCategory: Category? @State private var selectedRecipe: Recipe? @StateObject private var dataModel = DataModel()
var body: some View {
NavigationSplitView {
List(Category.allCases, selection: $selectedCategory) { category in
NavigationLink(category.localizedName, value: category)
}
.navigationTitle("Categories")
} detail: {
if let recipe = selectedRecipe {
RecipeDetail(recipe: recipe)
} else {
Text("Select a recipe")
}
}
}
}
2.2 Three-Column Layout Three-Column with Content Column NavigationSplitView { // Sidebar List(Category.allCases, selection: $selectedCategory) { category in NavigationLink(category.localizedName, value: category) } .navigationTitle("Categories") } content: { // Content column List(dataModel.recipes(in: selectedCategory), selection: $selectedRecipe) { recipe in NavigationLink(recipe.name, value: recipe) } .navigationTitle(selectedCategory?.localizedName ?? "Recipes") } detail: { // Detail column RecipeDetail(recipe: selectedRecipe) }
2.3 NavigationSplitView with NavigationStack (WWDC 2022, 14:10)
Combine split view selection with stack-based drill-down:
struct MultipleColumnsWithStack: View { @State private var selectedCategory: Category? @State private var path: [Recipe] = [] @StateObject private var dataModel = DataModel()
var body: some View {
NavigationSplitView {
List(Category.allCases, selection: $selectedCategory) { category in
NavigationLink(category.localizedName, value: category)
}
.navigationTitle("Categories")
} detail: {
NavigationStack(path: $path) {
RecipeGrid(category: selectedCategory)
.navigationDestination(for: Recipe.self) { recipe in
RecipeDetail(recipe: recipe)
}
}
}
.environmentObject(dataModel)
}
}
Key pattern: NavigationStack inside NavigationSplitView detail column enables grid-to-detail drill-down while preserving sidebar selection.
2.4 Column Visibility @State private var columnVisibility: NavigationSplitViewVisibility = .all
NavigationSplitView(columnVisibility: $columnVisibility) { Sidebar() } content: { Content() } detail: { Detail() }
// Programmatically control visibility columnVisibility = .detailOnly // Hide sidebar and content columnVisibility = .all // Show all columns columnVisibility = .automatic // System decides
2.5 Automatic Adaptation
NavigationSplitView automatically adapts:
iPad landscape All columns visible (depending on configuration) iPad portrait/Slide Over Collapses to overlay or single column iPhone Single navigation stack Apple Watch/TV Single navigation stack
Selection changes automatically translate to push/pop on iPhone.
2.6 iOS 26+ Liquid Glass Sidebar (WWDC 2025, 323) NavigationSplitView { List { ... } } detail: { DetailView() } // Sidebar automatically gets Liquid Glass appearance on iPad/macOS
// Extend content behind glass sidebar .backgroundExtensionEffect() // Mirrors and blurs content outside safe area
Deep Linking and URL Routing 3.1 Basic Deep Link Handling struct ContentView: View { @State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: Recipe.self) { recipe in
RecipeDetail(recipe: recipe)
}
.navigationDestination(for: Category.self) { category in
CategoryView(category: category)
}
}
.onOpenURL { url in
handleDeepLink(url)
}
}
func handleDeepLink(_ url: URL) {
// Parse URL: myapp://recipe/apple-pie
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let host = components.host else { return }
switch host {
case "recipe":
if let recipeName = components.path.dropFirst().description,
let recipe = dataModel.recipe(named: recipeName) {
path.removeLast(path.count) // Pop to root
path.append(recipe) // Push recipe
}
case "category":
if let categoryName = components.path.dropFirst().description,
let category = Category(rawValue: categoryName) {
path.removeLast(path.count)
path.append(category)
}
default:
break
}
}
}
3.2 Multi-Step Deep Links // URL: myapp://category/desserts/recipe/apple-pie func handleDeepLink(_ url: URL) { let pathComponents = url.pathComponents.filter { $0 != "/" }
path.removeLast(path.count) // Reset to root
var index = 0
while index < pathComponents.count {
let component = pathComponents[index]
switch component {
case "category":
if index + 1 < pathComponents.count,
let category = Category(rawValue: pathComponents[index + 1]) {
path.append(category)
index += 2
}
case "recipe":
if index + 1 < pathComponents.count,
let recipe = dataModel.recipe(named: pathComponents[index + 1]) {
path.append(recipe)
index += 2
}
default:
index += 1
}
}
}
State Restoration 4.1 Complete State Restoration (WWDC 2022, 18:12) struct UseSceneStorage: View { @StateObject private var navModel = NavigationModel() @SceneStorage("navigation") private var data: Data? @StateObject private var dataModel = DataModel()
var body: some View {
NavigationSplitView {
List(Category.allCases, selection: $navModel.selectedCategory) { category in
NavigationLink(category.localizedName, value: category)
}
.navigationTitle("Categories")
} detail: {
NavigationStack(path: $navModel.recipePath) {
RecipeGrid(category: navModel.selectedCategory)
}
}
.task {
// Restore on appear
if let data = data {
navModel.jsonData = data
}
// Save on changes
for await _ in navModel.objectWillChangeSequence {
data = navModel.jsonData
}
}
.environmentObject(dataModel)
}
}
4.2 Codable NavigationModel class NavigationModel: ObservableObject, Codable { @Published var selectedCategory: Category? @Published var recipePath: [Recipe] = []
enum CodingKeys: String, CodingKey {
case selectedCategory
case recipePathIds // Store IDs, not full objects
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(selectedCategory, forKey: .selectedCategory)
try container.encode(recipePath.map(\.id), forKey: .recipePathIds)
}
init() {}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.selectedCategory = try container.decodeIfPresent(Category.self, forKey: .selectedCategory)
// Convert IDs back to objects, discarding deleted items
let recipePathIds = try container.decode([Recipe.ID].self, forKey: .recipePathIds)
self.recipePath = recipePathIds.compactMap { DataModel.shared[$0] }
}
var jsonData: Data? {
get { try? JSONEncoder().encode(self) }
set {
guard let data = newValue,
let model = try? JSONDecoder().decode(NavigationModel.self, from: data)
else { return }
self.selectedCategory = model.selectedCategory
self.recipePath = model.recipePath
}
}
var objectWillChangeSequence: AsyncPublisher<Publishers.Buffer<ObservableObjectPublisher>> {
objectWillChange
.buffer(size: 1, prefetch: .byRequest, whenFull: .dropOldest)
.values
}
}
Key pattern: Store IDs, not full model objects. Use compactMap to handle deleted items gracefully.
Tab + Navigation Integration 5.1 Tab Syntax (iOS 18+) (WWDC 2024, 4:27) TabView { Tab("Watch Now", systemImage: "play") { WatchNowView() } Tab("Library", systemImage: "books.vertical") { LibraryView() } Tab(role: .search) { SearchView() } }
5.2 TabView with NavigationStack Per Tab TabView { Tab("Home", systemImage: "house") { NavigationStack { HomeView() .navigationDestination(for: Item.self) { item in ItemDetail(item: item) } } } Tab("Settings", systemImage: "gear") { NavigationStack { SettingsView() } } }
Key pattern: Each tab has its own NavigationStack to preserve navigation state when switching tabs.
5.3 Sidebar-Adaptable TabView (WWDC 2024, 6:41) TabView { Tab("Watch Now", systemImage: "play") { WatchNowView() } Tab("Library", systemImage: "books.vertical") { LibraryView() } TabSection("Collections") { Tab("Cinematic Shots", systemImage: "list.and.film") { CinematicShotsView() } Tab("Forest Life", systemImage: "list.and.film") { ForestLifeView() } } TabSection("Animations") { // More tabs... } Tab(role: .search) { SearchView() } } .tabViewStyle(.sidebarAdaptable)
Key features:
TabSection creates groups visible in sidebar .sidebarAdaptable enables sidebar on iPad, tab bar on iPhone Search tab with .search role gets special placement 5.4 Tab Customization (WWDC 2024, 10:45) @AppStorage("MyTabViewCustomization") private var customization: TabViewCustomization
TabView { Tab("Watch Now", systemImage: "play", value: .watchNow) { WatchNowView() } .customizationID("Tab.watchNow") .customizationBehavior(.disabled, for: .sidebar, .tabBar) // Can't be hidden
Tab("Optional Tab", systemImage: "star", value: .optional) {
OptionalView()
}
.customizationID("Tab.optional")
.defaultVisibility(.hidden, for: .tabBar) // Hidden by default
} .tabViewCustomization($customization)
5.5 Programmatic Tab Visibility
Use .hidden(_:) to show/hide tabs based on app state while preserving their navigation state.
State-Driven Tab Visibility enum AppContext { case home, browse }
struct ContentView: View { @State private var context: AppContext = .home @State private var selection: TabID = .home
var body: some View {
TabView(selection: $selection) {
Tab("Home", systemImage: "house") {
HomeView()
}
.tag(TabID.home)
Tab("Libraries", systemImage: "square.stack") {
LibrariesView()
}
.tag(TabID.libraries)
.hidden(context == .browse) // Hide in browse context
Tab("Playlists", systemImage: "music.note.list") {
PlaylistsView()
}
.tag(TabID.playlists)
.hidden(context == .browse)
Tab("Tracks", systemImage: "music.note") {
TracksView()
}
.tag(TabID.tracks)
.hidden(context == .home) // Hide in home context
}
.tabViewStyle(.sidebarAdaptable)
}
}
State Preservation
Key difference: .hidden(_:) preserves tab state, conditional rendering does not.
// ✅ State preserved when hidden Tab("Settings", systemImage: "gear") { SettingsView() // Navigation stack preserved } .hidden(!showSettings)
// ❌ State lost when condition changes if showSettings { Tab("Settings", systemImage: "gear") { SettingsView() // Navigation stack recreated } }
Common Patterns
Feature Flags
Tab("Beta Features", systemImage: "flask") { BetaView() } .hidden(!UserDefaults.standard.bool(forKey: "enableBetaFeatures"))
Authentication State
Tab("Profile", systemImage: "person.circle") { ProfileView() } .hidden(!authManager.isAuthenticated)
Purchase Status
Tab("Pro Features", systemImage: "star.circle.fill") { ProFeaturesView() } .hidden(!purchaseManager.isPro)
Development Builds
Tab("Debug", systemImage: "hammer") { DebugView() } .hidden(!isDevelopmentBuild)
private var isDevelopmentBuild: Bool { #if DEBUG return true #else return false #endif }
Animated Transitions
Wrap state changes in withAnimation for smooth tab bar layout transitions:
Button("Switch to Browse") { withAnimation { context = .browse selection = .tracks // Switch to first visible tab } } // Tab bar animates as tabs appear/disappear // Uses system motion curves automatically
5.6 iOS 26+ Tab Features (WWDC 2025, 256) // Tab bar minimization on scroll TabView { ... } .tabBarMinimizeBehavior(.onScrollDown)
// Bottom accessory view (always visible) TabView { ... } .tabViewBottomAccessory { PlaybackControls() }
// Dynamic visibility (recommended for mini-players) TabView { ... } .tabViewBottomAccessory(isEnabled: showMiniPlayer) { MiniPlayerView() .transition(.opacity) } // isEnabled: true = shows accessory // isEnabled: false = hides AND removes reserved space
// Search tab with dedicated search field Tab(role: .search) { SearchView() } // Morphs into search field when selected
5.7 Tab API Quick Reference Modifier Target iOS Purpose Tab(:systemImage:value:content:) — 18+ New tab syntax with selection value Tab(role: .search) — 18+ Semantic search tab with morph behavior TabSection(:content:) — 18+ Group tabs in sidebar view .customizationID(:) Tab 18+ Enable user customization .customizationBehavior(:for:) Tab 18+ Control hide/reorder permissions .defaultVisibility(:for:) Tab 18+ Set initial visibility state .hidden(:) Tab 18+ Programmatic visibility with state preservation .tabViewStyle(.sidebarAdaptable) TabView 18+ Sidebar on iPad, tabs on iPhone .tabViewCustomization($binding) TabView 18+ Persist user tab arrangement .tabBarMinimizeBehavior(_:) TabView 26+ Auto-hide on scroll .tabViewBottomAccessory(isEnabled:content:) TabView 26+ Dynamic content below tab bar iOS 26+ Navigation Features 6.1 Liquid Glass Navigation (WWDC 2025, 323)
Automatic adoption when building with Xcode 26:
Navigation bars become Liquid Glass Sidebars float above content with glass effect Tab bars float with new compact appearance Toolbars get automatic grouping 6.2 Background Extension Effect NavigationSplitView { Sidebar() } detail: { HeroImage() .backgroundExtensionEffect() // Content extends behind sidebar }
6.3 Bottom-Aligned Search (WWDC 2025, 256) NavigationSplitView { Sidebar() } detail: { DetailView() } .searchable(text: $query, prompt: "What are you looking for?") // Automatically bottom-aligned on iPhone, top-trailing on iPad
6.4 Scroll Edge Effect // Automatic blur effect when content scrolls under toolbar // Remove any custom darkening backgrounds - they interfere
// For dense UIs, adjust sharpness ScrollView { ... } .scrollEdgeEffectStyle(.soft) // .sharp, .soft
6.5 Tab Bar Minimization TabView { Tab("Home", systemImage: "house") { NavigationStack { ScrollView { // Content } } } } .tabBarMinimizeBehavior(.onScrollDown) // Minimizes on scroll
6.6 Sheet Presentations with Zoom Transition // Sheet morphs out of presenting button .toolbar { ToolbarItem { Button("Settings") { showSettings = true } .matchedTransitionSource(id: "settings", in: namespace) } } .sheet(isPresented: $showSettings) { SettingsView() .navigationTransition(.zoom(sourceID: "settings", in: namespace)) }
Router/Coordinator Patterns 7.1 When to Use Coordinators
Use coordinators when:
Navigation logic is complex with conditional flows Testing navigation in isolation Sharing navigation logic across multiple screens UIKit interop with heavy navigation requirements
Use built-in navigation when:
Simple linear or hierarchical navigation State restoration is primary concern Fewer than 5-10 navigation destinations No need for navigation unit testing 7.2 Simple Router Pattern // Route enum defines all possible destinations enum AppRoute: Hashable { case home case category(Category) case recipe(Recipe) case settings }
// Router class manages navigation @Observable class Router { var path = NavigationPath()
func navigate(to route: AppRoute) {
path.append(route)
}
func popToRoot() {
path.removeLast(path.count)
}
func pop() {
if !path.isEmpty {
path.removeLast()
}
}
}
// Usage in views struct ContentView: View { @State private var router = Router()
var body: some View {
NavigationStack(path: $router.path) {
HomeView()
.navigationDestination(for: AppRoute.self) { route in
switch route {
case .home:
HomeView()
case .category(let category):
CategoryView(category: category)
case .recipe(let recipe):
RecipeDetail(recipe: recipe)
case .settings:
SettingsView()
}
}
}
.environment(router)
}
}
// In child views struct RecipeCard: View { let recipe: Recipe @Environment(Router.self) private var router
var body: some View {
Button(recipe.name) {
router.navigate(to: .recipe(recipe))
}
}
}
7.3 Coordinator Pattern with Protocol protocol Coordinator { associatedtype Route: Hashable var path: NavigationPath { get set } func navigate(to route: Route) }
@Observable class RecipeCoordinator: Coordinator { typealias Route = RecipeRoute var path = NavigationPath()
enum RecipeRoute: Hashable {
case list(Category)
case detail(Recipe)
case edit(Recipe)
case relatedRecipes(Recipe)
}
func navigate(to route: RecipeRoute) {
path.append(route)
}
func showRecipeOfTheDay() {
path.removeLast(path.count)
if let recipe = DataModel.shared.recipeOfTheDay {
path.append(RecipeRoute.detail(recipe))
}
}
}
7.4 Testing Navigation // Router is easily testable func testNavigateToRecipe() { let router = Router() let recipe = Recipe(name: "Apple Pie")
router.navigate(to: .recipe(recipe))
XCTAssertEqual(router.path.count, 1)
}
func testPopToRoot() { let router = Router() router.navigate(to: .category(.desserts)) router.navigate(to: .recipe(Recipe(name: "Apple Pie")))
router.popToRoot()
XCTAssertTrue(router.path.isEmpty)
}
Testing Checklist Navigation Flow Testing All NavigationLinks navigate to correct destination Back button returns to previous view Pop to root clears entire stack Deep links navigate correctly from cold start Deep links navigate correctly when app is running State Restoration Testing Navigation state persists when app backgrounds Navigation state restores on app launch Deleted items handled gracefully (compactMap) SceneStorage key is unique per scene Multi-Platform Testing NavigationSplitView collapses correctly on iPhone Selection in sidebar pushes on iPhone Tab bar visible and functional on all platforms Sidebar toggle works on iPad iOS 26+ Testing Liquid Glass appearance correct Bottom-aligned search on iPhone Tab bar minimization works Scroll edge effect not interfering with custom backgrounds API Quick Reference NavigationStack NavigationStack { content } NavigationStack(path: $path) { content }
NavigationSplitView NavigationSplitView { sidebar } detail: { detail } NavigationSplitView { sidebar } content: { content } detail: { detail } NavigationSplitView(columnVisibility: $visibility) { ... }
NavigationLink NavigationLink(title, value: value) NavigationLink(value: value) { label }
NavigationPath path.append(value) path.removeLast() path.removeLast(path.count) path.count path.codable // For encoding NavigationPath(codableRepresentation) // For decoding
Modifiers .navigationTitle("Title") .navigationDestination(for: Type.self) { value in View } .searchable(text: $query) .tabViewStyle(.sidebarAdaptable) .tabBarMinimizeBehavior(.onScrollDown) .backgroundExtensionEffect()
Resources
WWDC: 2022-10054, 2024-10147, 2025-256, 2025-323
Skills: axiom-swiftui-nav, axiom-swiftui-nav-diag, axiom-swiftui-26-ref, axiom-liquid-glass
Last Updated Based on WWDC 2022-10054, WWDC 2024-10147, WWDC 2025-256, WWDC 2025-323 Platforms iOS 16+, iPadOS 16+, macOS 13+, watchOS 9+, tvOS 16+