SwiftUI:创建底部导航栏 tabBar
导航视图非常适合让我们创建分层的视图堆栈,以使用户能够向下获取数据,但对于显示不相关的数据而言效果不佳。为此,我们需要使用SwiftUI的 TabView
,它会在屏幕底部创建一个按钮条,点击每个按钮会显示一个不同的视图。
将选项卡放置在TabView
中就像将它们一一列出一样简单,就像这样:
TabView {
Text("Tab 1")
Text("Tab 2")
}
但是,实际上,您始终希望自定义选项卡的显示方式——在选项卡栏上方的代码中将有一个空白的灰色空间。尽管您可以点击该灰色空间的左右部分来激活两个选项卡,但这是非常糟糕的用户体验。
为了修改这个问题,最好将tabItem()
修饰符附加到TabView
内的每个视图。这使您可以自定义视图在选项卡栏中的显示方式,从而在其旁边提供图像和一些文本,如下所示:
TabView {
Text("Tab 1")
.tabItem {
Image(systemName: "star")
Text("One")
}
Text("Tab 2")
.tabItem {
Image(systemName: "star.fill")
Text("Two")
}
}
现在,您可以尝试将更多视图添加到tabItem()
修饰符中,或者重新排列它们,以使文本视图位于图像视图之前,但这并不重要:SwiftUI将始终显示不超过一个图像,并且按该顺序最多只能有一个文本视图。
除了让用户通过点击其标签项来切换视图之外,SwiftUI还允许我们使用状态以编程方式控制当前视图。这需要四个步骤:
- 创建一个
@State
属性以跟踪当前显示的选项卡。 - 每当我们想跳到另一个选项卡时,将该属性修改为一个新值。
- 将其作为绑定传递到
TabView
中,因此将自动对其进行跟踪。 - 告诉SwiftUI应该为该属性的每个值显示哪个选项卡。
其中的前三个很简单,因此我们将其排除在外。首先,我们需要某种状态来跟踪当前选项卡,因此将其作为属性添加到ContentView
:
@State private var selectedTab = 0
其次,我们需要在某处进行修改,这将要求SwiftUI切换选项卡。在我们的小演示中,我们可以将onTapGesture()
修饰符附加到第一个选项卡,如下所示:
Text("Tab 1")
.onTapGesture {
self.selectedTab = 1
}
.tabItem {
Image(systemName: "star")
Text("One")
}
第三,我们需要将TabView
的选择绑定到$selectedTab
。当我们创建TabView
时,它将作为参数传递,因此将您的代码更新为:
TabView(selection: $selectedTab) {
现在开始讨论有趣的部分:当我们说self.selectedTab = 1
时,SwiftUI如何知道Tab1是哪个Tab?您可能会认为这些Tab可以被视为一个数组,在这种情况下,第二个Tab将位于索引1处,但这会引起各种问题:如果我们将该 Tab 移至Tab 视图中的其他位置,该怎么办?
在更深层次上,它还分解了SwiftUI的核心概念之一:我们应该能够自由地组合视图。如果Tab 1是数组中的第二项,则:
- Tab 0是第一个tab。
- Tab 1是第二个tab。
- Tab 0具有显示Tab 1的
onTapGesture()
。 - 因此,Tab 0对如何配置其父项
TabView
具有深入的了解。
这是一个非常糟糕的主意,因此SwiftUI提供了更好的解决方案:我们可以为每个视图附加一个唯一的标识符,并将其用于选定的标签。这些标识符称为标签,并使用tag()
修饰符附加,如下所示:
Text("Tab 2")
.tabItem {
Image(systemName: "star.fill")
Text("Two")
}
.tag(1)
因此,我们的整体看起来是这样的:
struct ContentView: View {
@State private var selectedTab = 0
var body: some View {
TabView(selection: $selectedTab) {
Text("Tab 1")
.onTapGesture {
self.selectedTab = 1
}
.tabItem {
Image(systemName: "star")
Text("One")
}
.tag(0)
Text("Tab 2")
.tabItem {
Image(systemName: "star.fill")
Text("Two")
}
.tag(1)
}
}
}
现在该代码起作用了:您可以通过按选项卡上的选项卡或在第一个选项卡中激活我们的点击手势来在选项卡之间切换。
当然,仅使用0和1是不理想的——这些值是固定的,因此可以解决视图四处移动的问题,但它们不容易记住。幸运的是,您可以改用字符串:给每个视图一个唯一且反映其目的的字符串标记,然后将其用于@State
属性。从长远来看,这更容易使用,对比整数更建议使用。
提示:通常要同时使用NavigationView
和TabView
,但要注意:TabView
NavigationView