I am learning about swiftUI navigation stack and Tab Bars, most tutorials just show implementation of the tab bars. i am trying to hide my custom tab bar in subviews. Please can someone explain what to do so that I can learn and take note of it for future references. Thanks in advance
struct MainTabbedView: View {
@State var selectedTab = 0
@Environment(\.isTabBarVisible) var isTabBarVisible
var body: some View {
ZStack(alignment: .bottom) {
TabView(selection: $selectedTab) {
HomeView()
.tag(0)
FavoriteView()
.tag(1)
FantasyView_Preview()
.tag(2)
ProfileView()
.tag(3)
}
if isTabBarVisible {
ZStack {
HStack {
ForEach((TabbedItems.allCases), id: \.self) { item in
Button {
selectedTab = item.rawValue
} label: {
CustomTabItem(imageName: item.iconName, title: item.title, isActive: (selectedTab == item.rawValue))
}
}
}
.padding(6)
}
.frame(height: 70)
.background(Color.yinmnBlue.opacity(0.2))
.cornerRadius(35)
.padding(.horizontal, 26)
}
}
extension MainTabbedView {
func CustomTabItem(imageName: String, title: String, isActive: Bool) -> some View {
HStack(spacing: 10) {
Spacer()
Image(systemName:imageName)
.resizable()
.renderingMode(.template)
.foregroundColor(isActive ? .black : .gray)
.frame(width: 25, height: 25)
if isActive {
Text(title)
.font(.system(size: 14))
.foregroundColor(isActive ? .black : .gray)
}
Spacer()
}
.frame(width: isActive ? .infinity : 60, height: 60)
.background(isActive ? Color.yinmnBlue.opacity(0.4) : .clear)
.cornerRadius(30)
}
}
HomeView.swift
struct HomeView: View {
@State private var showsubview = false
var body: some View {
NavigationStack{
Button(action: {
showsubview = true
}, label: {
Text("Go to sub view")
})
.navigationDestination(isPresented: $showsubview) {
navigation1(isPresented: $showsubview)
.environment(\.isTabBarVisible, false)
}
}
}
}
i tried to use environment variables but it did not work
struct TabBarVisibilityKey: EnvironmentKey {
static let defaultValue: Bool = true
}
extension EnvironmentValues {
var isTabBarVisible: Bool {
get { self[TabBarVisibilityKey.self] }
set { self[TabBarVisibilityKey.self] = newValue }
}
}
Environment values are for passing information down the view hierarchy, but in this case you want subviews to inform their parents of whether they want the tab bar to be shown. You should use a preference instead.
First, write a preference key
struct CustomTabBarVisiblePreference: PreferenceKey {
// nil means "not set"
static let defaultValue: Bool? = nil
static func reduce(value: inout Bool?, nextValue: () -> Bool?) {
guard let next = nextValue() else { return }
value = next
}
}
In the navigation destination of HomeView
, set the preference to false:
struct HomeView: View {
@State private var showsubview = false
var body: some View {
NavigationStack{
Button(action: {
showsubview = true
}, label: {
Text("Go to sub view")
})
.navigationDestination(isPresented: $showsubview) {
Text("Destination")
// here:
.preference(key: CustomTabBarVisiblePreference.self, value: false)
}
}
}
}
In the tab view, use a overlayPreferenceValue
instead of a ZStack
to overlay the tab bar. This way, you can read the preference value and determine whether to show a tab bar.
struct MainTabbedView: View {
@State var selectedTab = 1
var body: some View {
TabView(selection: $selectedTab) {
HomeView()
.tag(0)
// I have replaced other views with simple Texts so that this is a minimal reproducible example
Text("Favourites View")
.tag(1)
Text("Fantasy View")
.tag(2)
Text("Profile View")
.tag(3)
}
.overlayPreferenceValue(CustomTabBarVisiblePreference.self, alignment: .bottom) { visible in
// "!= false" here so that the tab bar is visible if the preference is not set
if visible != false {
ZStack {
HStack {
ForEach(0..<4, id: \.self) { item in
Button {
selectedTab = item
} label: {
CustomTabItem(imageName: "circle", title: "Title \(item)", isActive: selectedTab == item)
}
}
}
.padding(6)
}
.frame(height: 70)
.background(Color.blue.opacity(0.2))
.cornerRadius(35)
.padding(.horizontal, 26)
}
}
}
}
// I refactored CustomTabItem as its own View struct
struct CustomTabItem: View {
let imageName: String
let title: String
let isActive: Bool
var body: some View {
HStack(spacing: 10) {
Spacer()
Image(systemName:imageName)
.resizable()
.renderingMode(.template)
.foregroundColor(isActive ? .black : .gray)
.frame(width: 25, height: 25)
if isActive {
Text(title)
.font(.system(size: 14))
.foregroundColor(isActive ? .black : .gray)
}
Spacer()
}
.frame(width: isActive ? nil : 60, height: 60)
.frame(maxWidth: isActive ? .infinity : nil)
.background(isActive ? Color.blue.opacity(0.4) : .clear)
.cornerRadius(30)
}
}