精通 SwiftUI - iOS 16 版

第 35 章
标签视图的运用与自订标签列

在现今最流行的 App中,例如 Facebook、Instagram 和 Twitter,你不难发现它们都使用标签介面。 这种介面在App底部会出现一个标签栏,让使用者可以在不同功能之间快速切换。 如使用 UIKit ,您可以利用UITabBarController 来建立标签栏界面。 SwiftUI 框架则提供了一个名为TabView的 UI 组件,供开发者在App制作标签介面。

在本章中,我们将向你解构 TabView 并建立标签栏界面、处理标签选择以及自订标签栏的外观。

使用 TabView 建立标签栏界面

假设您已经使用 Xcode 开启了一个 SwiftUI 项目,让我们从一个简单的Text视图开始,如下所示:

struct ContentView: View {
    var body: some View {
        Text("Home Tab")
            .font(.system(size: 30, weight: .bold, design: .rounded))
    }
}

要将这个文字视图嵌入到标签栏中,你只需要利用 TabView 组件包装它就可以。而通过附加 .tabItem 修饰器,你就可以设置标签项的描述,如下所示:

struct ContentView: View {
    var body: some View {
        TabView {
            Text("Home Tab")
                .font(.system(size: 30, weight: .bold, design: .rounded))
                .tabItem {
                    Image(systemName: "house.fill")
                    Text("Home")
                }
        }
    }
}

这就建立了一个带有选项的标签列。 在示例代码中,选项同时具有图像和文字,但您可以随意删除其中之一。

图 35.1. 示例标签列
图 35.1. 示例标签列

要显示更多选项,您只需在 TabView 中添加子视图,如下所示:

TabView {
    Text("Home Tab")
        .font(.system(size: 30, weight: .bold, design: .rounded))
        .tabItem {
            Image(systemName: "house.fill")
            Text("Home")
        }

    Text("Bookmark Tab")
        .font(.system(size: 30, weight: .bold, design: .rounded))
        .tabItem {
            Image(systemName: "bookmark.circle.fill")
            Text("Bookmark")
        }

    Text("Video Tab")
        .font(.system(size: 30, weight: .bold, design: .rounded))
        .tabItem {
            Image(systemName: "video.circle.fill")
            Text("Video")
        }

    Text("Profile Tab")
        .font(.system(size: 30, weight: .bold, design: .rounded))
        .tabItem {
            Image(systemName: "person.crop.circle")
            Text("Profile")
        }
}

这就建立了一个带有 4 个选项的标签列。

图 35.2. 4 个选项的标签列
图 35.2. 4 个选项的标签列

自订标签列颜色

在默认的情况下,标签列项目的颜色是设置为蓝色。 您可以通过将 .accentColor 修饰器附加到 TabView 来修改其颜色,如下所示:

TabView {

}
.accentColor(.red)

如果您将以上代码加到TabView,标签列的颜色就会修改为白色。

图 35.3. 修改标签列的颜色
图 35.3. 修改标签列的颜色

以编程方式切换不同选项

标签视图会自动处理使用者的选择,当使用者点击其中一个项目标签视图就会切换至相关项目视图。 在某些情况,您可能希望以写程序方式切换到特定选项。 TabView 有另一个用于此目的的 init 方法。 该方法需要一个包含选项卡标签值的状态变量:

TabView(selection: $selection)

举例,在 ContentView 中声明以下状态变量:

@State private var selection = 0

这里我们将 selection 变量的原始值设定为0,这是第一个选项的标签值。 我们还没有为每个选项定义标签值。 因此,像这样修改代码并为每个选项附加 tag 修饰器:

TabView(selection: $selection) {
    Text("Home Tab")
        .font(.system(size: 30, weight: .bold, design: .rounded))
        .tabItem {
            Image(systemName: "house.fill")
            Text("Home")
        }
        .tag(0)

    Text("Bookmark Tab")
        .font(.system(size: 30, weight: .bold, design: .rounded))
        .tabItem {
            Image(systemName: "bookmark.circle.fill")
            Text("Bookmark")
        }
        .tag(1)

    Text("Video Tab")
        .font(.system(size: 30, weight: .bold, design: .rounded))
        .tabItem {
            Image(systemName: "video.circle.fill")
            Text("Video")
        }
        .tag(2)

    Text("Profile Tab")
        .font(.system(size: 30, weight: .bold, design: .rounded))
        .tabItem {
            Image(systemName: "person.crop.circle")
            Text("Profile")
        }
        .tag(3)
}

我们通过附加 tag 修饰器为每个选项提供唯一索引。 TabView 也绑定到 selection 值。 要以编程方式切换到指定选项,就只需要修改 selection 变量的值。

您可以建立一个 Next 按钮来切换至下一个选项,如下所示:

ZStack(alignment: .topTrailing) {
    TabView(selection: $selection) {
       .
       .
       .
    }
    .accentColor(.red)

    Button {
        selection = (selection + 1) % 4
    } label: {
        Text("Next")
            .font(.system(.headline, design: .rounded))
            .padding()
            .foregroundColor(.white)
            .background(Color.red)
            .cornerRadius(10.0)
            .padding()
    }
}

进行修改后,在预览中运行App,您可以点击 Next 按钮跳至不同选项。

图 35.4. 点击 Next 按钮跳至不同选项
图 35.4. 点击 Next 按钮跳至不同选项

在导航视图中隐藏标签栏

你可以通过以下代码利用 NavigationStack 包裹 TabView 组件将标签视图嵌入导航视图中:

NavigationStack {
    TabView(selection: $selection) {
        .
        .
        .
    }

    .navigationTitle("TabView Demo")
}

在 UIKit 中,还有一个名为 hidesBottomBarWhenPushed 的选项,它允许您在使用导航界时自动隐藏标签栏。 SwiftUI 也内置了此功能。 您可以像这样修改代码:

NavigationStack {
    TabView(selection: $selection) {
        List(1...10, id: \.self) { index in
            NavigationLink(
                destination: Text("Item #\(index) Details"),
                label: {
                    Text("Item #\(index)")
                        .font(.system(size: 20, weight: .bold, design: .rounded))
                })

        }
        .listStyle(.plain)
        .tabItem {
            Image(systemName: "house.fill")
            Text("Home")
        }
        .tag(0)

        Text("Bookmark Tab")
            .font(.system(size: 30, weight: .bold, design: .rounded))
            .tabItem {
                Image(systemName: "bookmark.circle.fill")
                Text("Bookmark")
            }
            .tag(1)

        Text("Video Tab")
            .font(.system(size: 30, weight: .bold, design: .rounded))
            .tabItem {
                Image(systemName: "video.circle.fill")
                Text("Video")
            }
            .tag(2)

        Text("Profile Tab")
            .font(.system(size: 30, weight: .bold, design: .rounded))
            .tabItem {
                Image(systemName: "person.crop.circle")
                Text("Profile")
            }
            .tag(3)
    }
    .accentColor(.red)

    .navigationTitle("TabView Demo")
}

我们只是修改了 Home 选项卡的代码以显示项目列表。 我们用NavigationLink包装每个列表项目,以便在选择该项时导航至详细信息视图。 如果您使用模拟器或在预览中运行App,当导航到详细信息视图时,标签栏就会自动隐藏。

图 35.5. 隐藏标签栏
图 35.5. 隐藏标签栏

就某些情况,您可能不希望隐藏选标签栏。 如果是这个样的话,您可以反过来建立导航界面。 不是将标签视图包装在导航视图中,而是将导航视图嵌入到标签视图中,如下所示:

TabView(selection: $selection) {
    NavigationStack {
        List(1...10, id: \.self) { index in
            NavigationLink(
                destination: Text("Item #\(index) Details"),
                label: {
                    Text("Item #\(index)")
                        .font(.system(size: 20, weight: .bold, design: .rounded))
                })

        }

        .navigationTitle("TabView Demo")
    }
    .tabItem {
        Image(systemName: "house.fill")
        Text("Home")
    }
    .tag(0)

    .
    .
    .
}

现在,当您导航到项目的详细信息视图时,标签栏仍然存在。

总结

在本章中,我们向您介绍了 TabView 的基础知识,它是 SwiftUI 中用于建立标签介面的 UI 组件。 SwiftUI框架没有为您提供许多用于自订标签栏的选项。 但是,您仍然可依赖 UIKit 的 API 来自订其外观。

在本章所准备的示例档中,有最后完整的 Xcode 项目,可供你下载参考:

https://www.appcoda.com/resources/swiftui4/SwiftUITabView.zip