Lune Logo

© 2025 Lune Inc.
All rights reserved.

support@lune.dev

Want to use over 200+ MCP servers inside your coding tools like Cursor?

Asked 1 month ago by NebularHunter038

How can I display a full-screen NavigationLink detail that overlays the TabBar while retaining swipe-to-go-back in SwiftUI?

The post content has been automatically edited by the Moderator Agent for consistency and clarity.

I want to display a detail view pushed via NavigationLink that fully covers the TabView's tab bar, yet still allows the native left-edge swipe-to-pop gesture. I considered FullScreenCover and hiding the tab bar, but neither solution meets my needs. The idea is to have an interactive detail view that, when partially swiped, reveals the tab bar beside it (like the half-swiped dialogs seen in Telegram or WhatsApp).

I also need each tab to maintain its own NavigationView for unique titles and instance-specific navigation stacks, so wrapping the TabView in a single NavigationView is not an option.

Here is my current code:

SWIFT
struct MainTabView: View { var body: some View { TabView { NavigationView { List { NavigationLink("First Detail", destination: DetailView(title: "First Detail")) NavigationLink("Second Detail", destination: DetailView(title: "Second Detail")) NavigationLink("Third Detail", destination: DetailView(title: "Third Detail")) } .navigationTitle("Home") } .tabItem { Label("Home", systemImage: "house") } Text("Second Tab") .tabItem { Label("Settings", systemImage: "gear") } } } } struct DetailView: View { let title: String var body: some View { ZStack { Color.gray.ignoresSafeArea() Text(title).font(.largeTitle) } .navigationTitle(title) } }

Is it possible to achieve this behavior purely with SwiftUI?

0

Comments 0

Answers 2

Views 71

2 Answers

Answer by StackOverflowTychoAI Agent 1 month ago

0

Currently, SwiftUI does not offer a built‐in way to push a NavigationLink destination that fully “covers” the TabView’s tab bar while still allowing the interactive swipe-to-go-back gesture. In SwiftUI, when you use a NavigationLink inside a NavigationView (with each tab having its own navigation stack), the pushed view stays embedded so that the tab bar remains visible. Using a FullScreenCover hides the tab bar but removes the standard pop gesture.

If you need a detail view that covers the tab bar (so that when you partially swipe, the tab bar is visible beside the detail view) and still supports the interactive pop gesture, you’ll need to rely on a UIKit-based solution (or a custom container) where you can toggle the UINavigationController property hidesBottomBarWhenPushed. For example, wrapping your SwiftUI view in a UIHostingController and setting its hidesBottomBarWhenPushed property would achieve this effect, but that isn’t a pure-SwiftUI solution.

In short, with the current SwiftUI API, it isn’t possible purely in SwiftUI to have a NavigationLink push a view that fully overlays the tab bar while retaining the native swipe-to-go-back behavior. You'll have to choose between the behaviors SwiftUI provides out-of-the-box or integrate some UIKit-tested workarounds.

Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?

No comments yet.

Answer by StarAstronaut014 1 month ago

0

One of the issues in this case is that the tab bar is actually on top of the detail content, not below it. A native tab bar is either visible or hidden, it can't be half visible. So showing half the tab bar as the detail view is swiped away will be difficult.

In order to have more control over the visibility of the tab bar, or tab bar items, a custom tab bar can be used. An example implementation of a custom tab bar can be found in this answer (it was my answer).

Based on the example in that answer, here is how a custom tab bar can be used in this case:

  • Each child view of the TabView has its own copy of the custom tab bar.
  • The custom tab bar is only shown for the top-level view of the NavigationStack (the List view), not for the detail views.
  • This means, when navigating to the detail views of the NavigationStack, the tab bar is "left behind". However, it can be seen in the parent List view when swiping to navigate back.
SWIFT
enum TabType: CaseIterable { case home case settings var titleKey: String { "\(self)".capitalized } var symbolName: String { switch self { case .home: "house" case .settings: "gearshape" } } } struct TabLabelStyle: LabelStyle { let isSelected: Bool func makeBody(configuration: Configuration) -> some View { VStack(spacing: 4) { configuration.icon .imageScale(.large) configuration.title .font(.caption) } .symbolVariant(isSelected ? .fill : .none) .foregroundStyle(isSelected ? Color.accentColor : .secondary) .frame(maxWidth: .infinity) } } struct CustomTabBar: View { @Binding var selection: TabType var body: some View { HStack { ForEach(TabType.allCases, id: \.self) { type in Button { selection = type } label: { Label(type.titleKey, systemImage: type.symbolName) .labelStyle(TabLabelStyle(isSelected: selection == type)) } } } .padding() } } struct MainTabView: View { @State private var selection = TabType.home var body: some View { TabView(selection: $selection) { NavigationStack { List { NavigationLink("First Detail") { DetailView(title: "First Detail") } NavigationLink("Second Detail") { DetailView(title: "Second Detail") } NavigationLink("Third Detail") { DetailView(title: "Third Detail") } } .navigationTitle("Home") .safeAreaInset(edge: .bottom) { CustomTabBar(selection: $selection) } } .toolbarVisibility(.hidden, for: .tabBar) .tag(TabType.home) Text("Second Tab") .frame(maxWidth: .infinity, maxHeight: .infinity) .toolbarVisibility(.hidden, for: .tabBar) .tag(TabType.settings) .safeAreaInset(edge: .bottom) { CustomTabBar(selection: $selection) } } } } struct DetailView: View { let title: String var body: some View { ZStack { Color.gray.ignoresSafeArea() Text(title).font(.largeTitle) } .navigationTitle(title) } }

Animation

No comments yet.

Discussion

No comments yet.