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

如何在VisionOS中实现类似Disney+的自定义非透明背景TabBar?

实现VisionOS上类似Disney+的自定义TabBar

核心方案:自定义底部容器+TabView状态绑定

系统默认TabView在VisionOS中样式受限,很难实现非透明背景和完全自定义的图标布局。直接通过自定义底部栏组件,结合TabView的选中状态同步,就能解决你之前遇到的问题。

步骤1:统一管理Tab数据

先把每个Tab的图标、标签、对应视图封装成枚举,方便后续复用:

enum Tab: CaseIterable {
    case home, search, downloads, profile
    
    var title: String {
        switch self {
        case .home: return "首页"
        case .search: return "搜索"
        case .downloads: return "下载"
        case .profile: return "我的"
        }
    }
    
    var normalIcon: String {
        switch self {
        case .home: return "house"
        case .search: return "magnifyingglass"
        case .downloads: return "arrow.down.to.line"
        case .profile: return "person"
        }
    }
    
    var selectedIcon: String {
        switch self {
        case .home: return "house.fill"
        case .search: return "magnifyingglass.circle.fill"
        case .downloads: return "arrow.down.to.line.fill"
        case .profile: return "person.fill"
        }
    }
    
    @ViewBuilder
    var content: some View {
        switch self {
        case .home: HomeView()
        case .search: SearchView()
        case .downloads: DownloadsView()
        case .profile: ProfileView()
        }
    }
}

步骤2:编写自定义TabBar组件

做一个带非透明背景的底部栏,绑定选中状态,完全自定义图标样式:

struct CustomTabBar: View {
    @Binding var selectedTab: Tab
    
    var body: some View {
        HStack(spacing: 40) {
            ForEach(Tab.allCases, id: \.self) { tab in
                Button {
                    selectedTab = tab
                } label: {
                    VStack(spacing: 4) {
                        Image(systemName: selectedTab == tab ? tab.selectedIcon : tab.normalIcon)
                            .font(.title2)
                            .foregroundColor(selectedTab == tab ? .white : .gray)
                        Text(tab.title)
                            .font(.caption)
                            .foregroundColor(selectedTab == tab ? .white : .gray)
                    }
                }
            }
        }
        .padding(.vertical, 12)
        .background(Color.black.opacity(0.9)) // 非透明背景,可替换为任意颜色
        .cornerRadius(16)
        .padding(.horizontal, 24)
        .padding(.bottom, 16) // 适配VisionOS底部安全区
    }
}

步骤3:整合到主视图

用ZStack把TabView和自定义TabBar叠加,隐藏系统默认TabBar:

struct MainTabContainer: View {
    @State private var selectedTab: Tab = .home
    
    var body: some View {
        ZStack(alignment: .bottom) {
            TabView(selection: $selectedTab) {
                ForEach(Tab.allCases, id: \.self) { tab in
                    tab.content
                        .tag(tab)
                }
            }
            .tabViewStyle(.page(indexDisplayMode: .never)) // 隐藏系统默认TabBar
            
            CustomTabBar(selectedTab: $selectedTab)
        }
        .edgesIgnoringSafeArea(.bottom) // 让背景延伸至底部边缘
    }
}

解决你之前的问题

  • Ornament方案的问题:Ornament在VisionOS中是悬浮组件,无法稳定固定在底部中心,且和TabView状态同步逻辑繁琐。上述方案直接将TabBar作为底部固定组件,和TabView状态完全绑定。
  • TabItem加偏移的问题:系统TabItem受VisionOS样式限制,无法彻底修改背景透明度,自定义偏移容易导致布局错乱。自定义TabBar完全脱离系统样式,可自由控制背景、图标位置和大小。

额外优化:添加中间突出按钮(仿Disney+播放按钮)

如果需要类似Disney+的中间突出按钮,修改CustomTabBar的HStack即可:

struct CustomTabBar: View {
    @Binding var selectedTab: Tab
    
    var body: some View {
        HStack(spacing: 40) {
            // 左侧两个Tab按钮
            ForEach([Tab.home, Tab.search], id: \.self) { tab in
                Button {
                    selectedTab = tab
                } label: {
                    VStack(spacing: 4) {
                        Image(systemName: selectedTab == tab ? tab.selectedIcon : tab.normalIcon)
                            .font(.title2)
                            .foregroundColor(selectedTab == tab ? .white : .gray)
                        Text(tab.title)
                            .font(.caption)
                            .foregroundColor(selectedTab == tab ? .white : .gray)
                    }
                }
            }
            
            // 中间突出按钮
            Button {
                // 自定义点击逻辑,比如打开播放页面
            } label: {
                Image(systemName: "play.circle.fill")
                    .font(.system(size: 48))
                    .foregroundColor(.red)
                    .shadow(radius: 4)
            }
            
            // 右侧两个Tab按钮
            ForEach([Tab.downloads, Tab.profile], id: \.self) { tab in
                Button {
                    selectedTab = tab
                } label: {
                    VStack(spacing: 4) {
                        Image(systemName: selectedTab == tab ? tab.selectedIcon : tab.normalIcon)
                            .font(.title2)
                            .foregroundColor(selectedTab == tab ? .white : .gray)
                        Text(tab.title)
                            .font(.caption)
                            .foregroundColor(selectedTab == tab ? .white : .gray)
                    }
                }
            }
        }
        .padding(.vertical, 12)
        .background(Color.black.opacity(0.9))
        .cornerRadius(16)
        .padding(.horizontal, 24)
        .padding(.bottom, 16)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 20:34:50