You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中Bottom Tab Bar无法在所有页面显示的解决方案咨询

实现所有页面(Tab关联页+侧边栏页)显示Bottom Tab Bar的方案

我的Bottom Tab Bar目前仅在Home、Search、Profile、Settings这些TabView关联页面可见,从这些页面跳转的子页面也能正常显示Tab Bar,但侧边栏(SidebarView)中的其他页面无法显示Tab Bar。现在需要调整实现,让所有页面(包括Tab关联页和侧边栏页面)都能显示Bottom Tab Bar。

核心原因

当前结构中,侧边栏的页面是通过NavigationLink、sheet或fullScreenCover直接从SidebarView跳转的,这些页面不在TabBar的TabView层级内,因此不会继承Tab Bar的显示逻辑。要解决问题,需将所有页面纳入TabBar的导航层级中,让侧边栏的跳转操作在TabBar的导航栈内进行。

解决方案及修改代码

1. 重构SidebarView的跳转逻辑

将侧边栏内的直接跳转改为通过绑定变量通知TabBar处理,确保所有页面都处于TabBar的导航层级下:

struct SidebarView: View {
    @Environment(\.presentationMode) var presentationMode
    // 添加绑定变量,用于通知TabBar跳转目标页面
    @Binding var navigateToPage: String?
    @Binding var showTicketsSheet: Bool
    @Binding var showParkMap: Bool
    
    @State private var isDropdownOpen = false
    @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
    
    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) {
                        // 重构购票页跳转
                        Button(action: {
                            showTicketsSheet = true
                            presentationMode.wrappedValue.dismiss()
                        }) {
                            Text("book_tickets".localized)
                                .foregroundColor(Color("menucolor"))
                                .font(Font.custom(Singleton.shared.setMediumCustomFont("DMSans-Medium"), size: 20))
                                .padding(.top, 10)
                        }
                        .alignmentGuide(.leading) { _ in 270 }
                        
                        // 重构地图页跳转
                        Button(action: {
                            showParkMap = true
                            presentationMode.wrappedValue.dismiss()
                        }) {
                            Text("park_map".localized)
                                .foregroundColor(Color("menucolor"))
                                .font(.system(size: 18))
                                .padding(.top, 10)
                        }
                        .alignmentGuide(.leading) { _ in 270 }
                        
                        // 重构RigView跳转
                        Button(action: {
                            navigateToPage = "RigView"
                            presentationMode.wrappedValue.dismiss()
                        }) {
                            Text("Page2")
                                .foregroundColor(Color("menucolor"))
                                .font(.system(size: 18))
                                .padding(.top, 10)
                        }
                        .alignmentGuide(.leading) { _ in 270 }
                    }
                    Spacer()
                        .frame(height: max(0, geometry.size.height - 700))
                }
            }
            .foregroundColor(Color("menucolor"))
            .font(Font.custom(Singleton.shared.setSemiBoldCustomFont("DMSans-Medium"), size: 20))
            .fontWeight(.semibold)
        }
    }
}

2. 修改TabBar结构,统一导航层级

将所有页面纳入TabBar的导航栈,并处理侧边栏触发的跳转:

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
    // 导航相关绑定变量
    @State private var navigateToPage: String?
    @State private var showParkMap: Bool = false
    
    var body: some View {
        NavigationView {
            ZStack(alignment: .bottom) {
                TabView(selection: $selectedTab) {
                    // 每个Tab页面嵌套NavigationStack,确保跳转后TabBar可见
                    NavigationStack {
                        Home()
                            .navigationDestination(item: $navigateToPage) { page in
                                if page == "RigView" {
                                    RigView().navigationBarBackButtonHidden()
                                }
                            }
                    }
                    .tag(0)
                    .id(contentID)
                    
                    NavigationStack {
                        SearchView(selectedTab: $selectedTab)
                    }
                    .tag(1)
                    
                    NavigationStack {
                        Profile()
                    }
                    .tag(3)
                    
                    NavigationStack {
                        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
                    }
                }
                
                // TabBar视图保持不变
                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)
                }
                .fullScreenCover(isPresented: $showParkMap) {
                    ParkMap()
                        .presentationBackground(Color.black.opacity(0.65))
                }
                
                // 侧边栏视图,传递绑定变量
                if isSidebarOpen {
                    SidebarView(navigateToPage: $navigateToPage, showTicketsSheet: $isTicketSheetPresented, showParkMap: $showParkMap)
                        .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()
                            }
                        }) {
                            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)
        }
    }
}

关键修改说明

  • 侧边栏跳转逻辑重构:将直接跳转改为通过绑定变量通知TabBar处理,确保所有页面都处于TabBar的导航层级内。
  • Tab页面嵌套NavigationStack:每个Tab子页面都添加NavigationStack,保证跳转后的子页面仍在同一导航栈中,TabBar不会被隐藏。
  • 统一弹窗管理:将侧边栏的弹窗触发逻辑移到TabBar中统一处理,弹窗显示时TabBar依然可见。

内容的提问来源于stack exchange,提问作者Aruna

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 21:30:07