The below are my sidebar view and bottom tab bar view..The tab bar is visible only in the views that is associated with that(I mean home, search, profile and settings but it is visible in the pages that are redirecting from here)…but not visible in the other that is given in the side bar view. So what is the possible way to show the tab bar in all of the views both in the tab bar and the views given in the side bar.
Side bar View :
struct SidebarView: View {
@Environment(\.presentationMode) var presentationMode
@State private var isDropdownOpen = false
@State private var isTicketsPagePresented = false
@State private var isParkMapPopupPresented = false
@State private var ticketsPageOffset: CGSize = .zero
@State private var date: String = ""
@State private var time: String = ""
@State private var adultCount: Int = 0
@State private var childCount: Int = 0
@EnvironmentObject var layoutDirectionManager: LayoutDirectionManager
@State private var isSidebarOpen = false
var body: some View {
GeometryReader { geometry in
VStack(alignment: .leading, spacing: 20) {
ScrollView(showsIndicators: false) {
ZStack(alignment: .leading) {
RoundedRectangle(cornerRadius: 33)
.fill(Color("welcomebg"))
.frame(width: 320, height: isDropdownOpen ? 155 : 60, alignment: .center)
.shadow(radius: 0.8)
.background(Color.white)
.padding(.leading, 10)
Group {
HStack(alignment: .center, spacing: 125) {
Text("welcome".localized)
.foregroundColor(Color("welcometext"))
.font(Font.custom(Singleton.shared.setBoldCustomFont("Hobeaux-bold"), size: 20))
.offset(y: isDropdownOpen ? -30 : 0)
.padding(.leading, 10)
Button(action: {
isDropdownOpen.toggle()
}) {
HStack {
Text(Singleton.shared.retrieveString(for: .selectedLanguage) == LanguageEnum.arabic.languageCode ? LanguageEnum.arabic.title : LanguageEnum.english.title)
Image(systemName: isDropdownOpen ? "chevron.up" : "chevron.down")
}
.foregroundColor(.black)
.font(.system(size: 18))
.fontWeight(.light)
}
.offset(x : Singleton.shared.retrieveString(for: .selectedLanguage) == "en" ? 0 : 50 ,y: isDropdownOpen ? -30 : 0)
}
.padding(.horizontal, 15)
if isDropdownOpen {
Divider().frame(width: 260,height: 0.5).overlay(.black).padding(.horizontal, 25)
VStack(alignment: .leading, spacing: 8) {
ForEach(0..<Singleton.shared.languages.count, id: \.self) { index in
Text(Singleton.shared.languages[index])
.font(.system(size: 20))
.padding(.top, 10)
.onTapGesture {
Singleton.shared.storeString(value: Singleton.shared.languages[index] == LanguageEnum.english.title ? LanguageEnum.english.languageCode : LanguageEnum.arabic.languageCode, in: .selectedLanguage)
layoutDirectionManager.toggleLayoutDirection()
isDropdownOpen.toggle()
}
}
}
.padding(.top, 75)
.padding(.horizontal, 30)
}
}
}
.padding(.top, 10)
VStack(alignment: .leading, spacing: 15) {
let menuItems: [(String, AnyView)] = [
(“Page”1.localized, TicketsPage()),
(“Page”2, AnyView(RigView().navigationBarBackButtonHidden())),
]
ForEach(menuItems, id: \.0) { menuItem in
if menuItem.0 == "book_tickets".localized {
Button(action: {
self.isTicketsPagePresented = true
}) {
Text(menuItem.0)
.foregroundColor(Color("menucolor"))
.font(Font.custom(Singleton.shared.setMediumCustomFont("DMSans-Medium"), size: 20))
.padding(.top, 10)
}
.alignmentGuide(.leading) { _ in 270 }
} else if menuItem.0 == "park_map".localized {
Button(action: {
isParkMapPopupPresented.toggle()
presentationMode.wrappedValue.dismiss()
}) {
Text(menuItem.0)
.foregroundColor(Color("menucolor"))
.font(.system(size: 18))
.padding(.top, 10)
}
.alignmentGuide(.leading) { _ in 270 }
} else {
NavigationLink(destination: menuItem.1) {
Text(menuItem.0)
.foregroundColor(Color("menucolor"))
.font(.system(size: 18))
.padding(.top, 10)
}
.alignmentGuide(.leading) { _ in 270 }
}
}
.fullScreenCover(isPresented: $isParkMapPopupPresented) {
ParkMap()
.presentationBackground(Color.black.opacity(0.65))
}
}
Spacer()
.frame(height: max(0, geometry.size.height - 700))
}
}
.foregroundColor(Color("menucolor"))
.font(Font.custom(Singleton.shared.setSemiBoldCustomFont("DMSans-Medium"), size: 20))
.fontWeight(.semibold)
.sheet(isPresented: $isTicketsPagePresented) {
TicketsPage()
}
}
}
}
Tab bar view :
struct TabBar: View {
@State var selectedTab = 0
@State private var contentID = UUID()
@State private var date: String = ""
@State private var adult: Int = 0
@State private var child: Int = 0
@State private var isTicketSheetPresented = false
@State private var shouldResetHome = false
@State private var isSidebarOpen = false
var body: some View {
NavigationView {
ZStack(alignment: .bottom) {
TabView(selection: $selectedTab) {
Home()
.tag(0)
.id(contentID)
SearchView(selectedTab: $selectedTab)
.tag(1)
Profile()
.tag(3)
Settings()
.tag(4)
}
.onChange(of: selectedTab, perform : {
value in
DispatchQueue.main.async {
self.contentID = UUID()
}
if value == 2 {
isTicketSheetPresented = true
}
})
.onChange(of: isTicketSheetPresented) { newValue in
if newValue == false {
selectedTab = 0
}
}
TabBarShape(insetRadius: 25)
.frame(width: 360, height: 72)
.foregroundColor(.white)
.shadow(color: Color(white: 0.8), radius: 6, x: 0, y: 3)
.offset(y: 20)
Button {
selectedTab = 2
} label: {
CustomTabItem(imageName: "ticket", title: "Ticket", isActive: (selectedTab == 2))
}
.frame(width: 55, height: 55, alignment: .center)
.background(LinearGradient(colors: [Color("lg31"), Color("lg32")], startPoint: .top, endPoint: .bottom))
.clipShape(Circle())
.shadow(radius: 0.8)
.offset(y: -35)
HStack(alignment: .center) {
ForEach(TabbedItems.allCases, id: \.self) { item in
if item != .ticket {
Button {
if item == .home {
shouldResetHome.toggle()
}
if selectedTab == item.rawValue {
DispatchQueue.main.async {
self.contentID = UUID()
}
}
selectedTab = item.rawValue
} label: {
CustomTabItem(imageName: item.iconName, title: item.title, isActive: (selectedTab == item.rawValue))
}
}
}
}
.blur(radius: isSidebarOpen ? 2 : 0)
.sheet(isPresented: $isTicketSheetPresented) {
TicketsPage(date: $date, adultCount: $adult, childCount: $child)
.presentationBackground(.clear)
}
if isSidebarOpen {
SidebarView()
.frame(width:UIScreen.main.bounds.width / 1.17)
.background(Color.white)
.transition(.move(edge: .leading))
.onTapGesture {
withAnimation {
self.isSidebarOpen.toggle()
}
}
.offset(x: UIScreen.main.bounds.width - 420)
}
}
.ignoresSafeArea(.keyboard, edges: [.bottom, .top])
.toolbar {
ToolbarItem(placement: .primaryAction) {
HStack(alignment: .center, spacing: 250) {
Image("LaunchImage")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 55, height: 55)
Button(action: {
withAnimation {
self.isSidebarOpen.toggle()
}
print("Menu Button Tapped")
}) {
Image(isSidebarOpen ? "close" : "menu")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 35, height: 35)
.padding(.bottom, 3)
}
}
}
}
}
}
func CustomTabItem(imageName: String, title: String, isActive: Bool) -> some View {
HStack(spacing: 0) {
Spacer(minLength: 15)
if imageName == "ticket" {
LottieView(loopMode: .loop, fileName: "ticket")
.frame(width: 40, height: 40, alignment: .center)
} else {
Image(imageName)
.resizable()
.renderingMode(.template)
.foregroundColor(imageName == "ticket" ? .white : (isActive ? .purple : Color("tabitem")))
.frame(width: 23, height: 23, alignment: .center)
.padding(5)
}
Spacer(minLength: 15)
}
}
}
SideBarView
is NOT a subview ofTabView
. You're expecting an illegitimate behavior.
1. To achieve this you might want to add the SideBarView
to every subview in TabView
. You can achieve this by creating a TabItemView
that gets a content block and a adds the SideBarView
.
struct TabViewItem<Content: View>: View {
// This is your TabViewItem (home, search, profile or settings)
@ViewBuilder let content: () -> Content
@State private var isSidebarOpen = false
var body: some View {
ZStack {
content()
if isSidebarOpen {
SidebarView()
}
}
}
}
Usage:
TabView(selection: $selectedTab) {
TabViewItem {
Home()
}
.tag(0)
.id(contentID)
TabViewItem {
SearchView(selectedTab: $selectedTab)
}
.tag(1)
TabViewItem {
Profile()
}
.tag(3)
TabViewItem {
Settings()
}
.tag(4)
}
2. You can also achieve this by creating a custom ViewModifier
that adds an overlay layer to every subview of TabBar
.
struct SideBar: ViewModifier {
@Binding var open: Bool
func body(content: Content) -> some View {
content
.overlay(alignment: .leading) {
if open {
SidebarView()
}
}
}
}
extension View {
func sideBar(open: Binding<Bool>) -> some View {
modifier(SideBar(open: open))
}
}
Usage:
TabView(selection: $selectedTab) {
Home()
.tag(0)
.id(contentID)
.sideBar(open: $isSidebarOpen)
SearchView(selectedTab: $selectedTab)
.tag(1)
.sideBar(open: $isSidebarOpen)
Profile()
.tag(3)
.sideBar(open: $isSidebarOpen)
Settings()
.tag(4)
.sideBar(open: $isSidebarOpen)
}