How to force a view opened by NavigationLink
to be fullscreen? I don't want the tab bar to be visible below. I want to keep the ability to swipe from the left side of the screen to go back, so FullScreenCover
is not an option. Hiding the tab bar when the details view is open is not a good solution either, since I want the details view to cover the tab bar. In other words, the tab bar should be partially visible beside the details view when the details view is half-swiped to the right. Eventually I want it behaves something like you can see when you half-swipe opened dialog in telegram or whatsapp.
Is it even possible purely with swiftUI?
I also want to keep a separate NavigationView
for each tab since I want them to have unique titles, etc. So I don't really want to wrap the TabView
into single NavigationView
.
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)
}
}
How to force a view opened by NavigationLink
to be fullscreen? I don't want the tab bar to be visible below. I want to keep the ability to swipe from the left side of the screen to go back, so FullScreenCover
is not an option. Hiding the tab bar when the details view is open is not a good solution either, since I want the details view to cover the tab bar. In other words, the tab bar should be partially visible beside the details view when the details view is half-swiped to the right. Eventually I want it behaves something like you can see when you half-swipe opened dialog in telegram or whatsapp.
Is it even possible purely with swiftUI?
I also want to keep a separate NavigationView
for each tab since I want them to have unique titles, etc. So I don't really want to wrap the TabView
into single NavigationView
.
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)
}
}
Share
Improve this question
edited Feb 8 at 8:19
routern
asked Feb 8 at 8:11
routernroutern
51 silver badge2 bronze badges
4
|
1 Answer
Reset to default 0One 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
(theList
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 parentList
view when swiping to navigate back.
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)
}
}
NavigationView
is deprecated. If you do not need to support iOS 15 then it would be better to be using aNavigationStack
. If you do need to support iOS 15, this would be useful to know. – Benzy Neez Commented Feb 8 at 9:03