VisionPro开发 - Tab Bar & Side Bar

1,115 阅读2分钟

首页:漫游Apple Vision Pro

Code Repo: github.com/xuchi16/vis…

Project Path: github.com/xuchi16/vis…


基本概念

苹果的各个OS生态中,有各种不同的导航栏、工具栏组件。不同组件适用的平台也不完全相同。

根据官方文档总结如下:

DefinitioniOSiPadOSMacOStvOSvisionOSwatchOS
Tab barTab bar docTab bars use bar items to navigate between mutually exclusive panes of content in the same view.×Highly customizable×
Tab viewTab view docA tab view presents multiple mutually exclusive panes of content in the same area, which people can switch between using a tabbed control.××××
Side barSide bar docA sidebar can help people navigate your app or game, providing quick access to top-level collections of content.Consider using a tab bar instead of a sidebarNo additional considerations for tvOS×
Tool barTool bar docA toolbar provides convenient access to frequently used commands and controls that perform actions relevant to the current view.Offers commonly used actionsHighly customizable
OrnamentsOrnaments docAn ornament presents controls and information related to a window, without crowding or obscuring the window’s contents.×××××

这里我们先讨论Tab bar和Side bar两种形式。

Tab bar

Tab bar会在同一区域呈现多个互斥的内容面板,用户可以通过标签控件进行切换。手机屏幕较高,因此在iOS中,b bar在屏幕底部。而在visionOS中,由于不受屏幕尺寸的限制,所以通过悬浮于窗口外的侧边栏实现。

image.png

Side bar

Side bar则更适用于宽屏幕设备,如macOS,iPadOS以及visionOS。

image.png

因此对于visionOS而言,可以同时有Tab bar和Side bar两层结构进行导航操作。

实现

Tab bar

Tab bar提供多个View的切换。实现中顶层是一个TabView<SelectionValue, Content>。其中SelectionValue表示当前选中的View,Content表示它包含的子页面。

对于每个子页面,需要通过.tag.tabItem两个modifier提供更具体的信息

  • .tag为每个View提供唯一的tag
  • .tabItem提供图标和文案。默认展示图标,用户注视时图标会展开,显示详细的文案

这样,我们就可以获得一个Tab bar。

TabView(selection: $barIdx,
        content:  {
    Text("Tab Content 1")
        .tabItem {
            Label("Albums", systemImage: "rectangle.stack.fill")
        }
        .tag(1)
    Text("Tab Content 2")
        .tabItem {
            Label("Memories", systemImage: "memories")
        }
        .tag(2)
    // ...
})

数据可以通过@Environment以注入的方式传递,并且为每个选项渲染对应的页面。

struct ContentView: View {
    
    @State var barIdx = 1
    @Environment(ViewModel.self) var viewModel;
    
    var body: some View {
        
        @Bindable var viewModel = viewModel
        
        TabView(selection: $barIdx, content: {
            ForEach(viewModel.models) { model in
                PageView(dataModel : model)
                    .tabItem { Label(model.labelName, systemImage: model.labelImage) }
                    .tag(model.id)
            }
        })
    }
}

Side bar

Side bar顶层是NavigationSplitView,它包含2-3列,其中第一列控制后几列的展示。每一个选项页面是一个NavigationLink

NavigationSplitView() {
    List {
        NavigationLink {
            Text(dataModel.description)
            Text("Recent view")
        } label: {
            Label("Recent", systemImage: "clock")
        }
        NavigationLink {
            Text(dataModel.description)
            Text("Favorite view")
        } label: {
            Label("Favorite", systemImage: "suit.heart")
        }
    }
    .navigationTitle(dataModel.labelName)
} detail: {
    Text("Select an item")
}

这样我们就可以获得一个拥有2层级导航的页面。