精通 SwiftUI - iOS 17 版

第 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 個選項的標籤列

自訂標籤列顏色

在預設的情況下,標籤列項目的顏色是設置為藍色。 您可以通過將 .tint 修飾器附加到 TabView 來更改其顏色,如下所示:

TabView {

}
.tint(.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) {
       .
       .
       .
    }
    .tint(.red)

    Button {
        selection = (selection + 1) % 4
    } label: {
        Text("Next")
            .font(.system(.headline, design: .rounded))
            .padding()
            .foregroundStyle(.white)
            .background(.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)
    }
    .tint(.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/swiftui5/SwiftUITabView.zip