SwiftUI 框架

在 SwiftUI 利用 ToggleStyle 簡單建立客製化的切換按鈕

Toggle 是 SwiftUI 中很常用的視圖,讓使用者在開或關兩種狀態之間切換。這個控件可以為使用者提供簡單而直觀的界面,因此很多開發者都會用到它。在這篇文章中,我會帶大家在 SwiftUI 使用 ToggleStyle 協定,輕鬆地創建適合自己 App 設計和風格的 Toggle。
在 SwiftUI 利用 ToggleStyle 簡單建立客製化的切換按鈕
Photo by Isaac Li Shung Tan on Unsplash
In: SwiftUI 框架

Toggle 是 SwiftUI 中很常用的視圖,讓使用者在開或關兩種狀態之間切換。這個控件 (control) 可以為使用者提供簡單而直觀的界面,因此很多開發者都會用到它。舉個例子,我們可以在 App 中使用 Toggle,來控制開啟或關閉某些功能,或切換不同模式或選項。要在 SwiftUI App 中實作 Toggle 十分簡單,我們可以使用 ToggleStyle 協定,來創建適合我們 App 設計和風格的 Toggle。

在這篇教學文章中,我會帶大家了解這個 Toggle 視圖,並一起利用 ToggleStyle 構建幾個客製化 Toggle。

Toggle 的基礎使用

SwiftUI Sample Toggles

在 SwiftUI 中,我們有幾個方法可以使用 Toggle 視圖。最基本的使用方法是這樣的:

struct ContentView: View {
    @State private var isEnabled = false

    var body: some View {
        Toggle("Airplane Mode", isOn: $isEnabled)
    }
}

在這裡,我們使用 text description 和 binding 來初始化 Toggle 視圖,而 Toggle 的狀態是由狀態變數 (state variable) 所控制的。這段程式碼會建構出上圖那個帶有 text label 的基本 toggle。

如果我們想客製化文本的樣式,Toggle 還有另一個初始化器 (initializer)。我們可以在閉包中這樣設定 Text 視圖的外觀:

Toggle(isOn: $isEnabled) {
    Text("Airplane mode")
        .font(.system(.title, design: .rounded))
        .bold()
}

我們還可以這樣客製化 description,來添加一個圖像:

Toggle(isOn: $isEnabled) {
    HStack {
        Text("Airplane mode")
        Image(systemName: "airplane")

    }
    .font(.system(size: 20))
}

最後我們會看到一個更漂亮的 switch。

swiftui-switch-with-image

改變 Toggle 的樣式

在預設設定下,Toggle 視圖是使用 switch 樣式的。我們可以使用 .toggleStyle 修飾符,來把樣式從 switch 轉換成 button

.toggleStyle(.button)

比如說,如果我們想建立一個書籤按鈕,就可以使用以下的程式碼:

struct ContentView: View {
    @State private var isBookmarked = false

    var body: some View {
        Toggle(isOn: $isBookmarked) {
            Image(systemName: isBookmarked ? "bookmark.fill" : "bookmark")
                .font(.system(size: 50))
        }
        .tint(.green)
        .toggleStyle(.button)
        .clipShape(Circle())
    }
}

整個操作十分相似,只是這次我們會用 .toggleStyle 修飾符,來告訴 Toggle 使用 .button 樣式。如此一來,SwiftUI 就不會把 toggle 顯示為一個開關,而是會顯示為一個按鈕,而按鈕會根據狀態改變其外觀。

swiftui-toggle-button-style

使用 ToggleStyle 來建立一個客製化 Toggle

在大部分情況下,預設的 toggle 樣式都足夠應付我們的需要。不過,在某些情況下,我們可能想要創建客製化的樣式,來切合 App 的風格。接下來,讓我們看看如何創建自己的客製化 toggle 吧!簡單來說,我們可以採用 ToggleStyle 協定,並實作所需的 makeBody(configuration:) 函數,來創建自己的樣式:

struct CustomToggleStyle: ToggleStyle {
    func makeBody(configuration: Configuration) -> some View {
        // Your implementation
    }
}

讓我們從簡單的開始!首先,我們會構建一個 toggle switch,並客製化其背景顏色和符號。

swiftui-togglestyle-example

要構建這個 toggle switch,讓我們建立一個新的結構 SymbolToggleStyle,它符合 ToggleStyle 協定:

struct SymbolToggleStyle: ToggleStyle {

    var systemImage: String = "checkmark"
    var activeColor: Color = .green

    func makeBody(configuration: Configuration) -> some View {
        HStack {
            configuration.label

            Spacer()

            RoundedRectangle(cornerRadius: 30)
                .fill(configuration.isOn ? activeColor : Color(.systemGray5))
                .overlay {
                    Circle()
                        .fill(.white)
                        .padding(3)
                        .overlay {
                            Image(systemName: systemImage)
                                .foregroundColor(configuration.isOn ? activeColor : Color(.systemGray5))
                        }
                        .offset(x: configuration.isOn ? 10 : -10)

                }
                .frame(width: 50, height: 32)
                .onTapGesture {
                    withAnimation(.spring()) {
                        configuration.isOn.toggle()
                    }
                }
        }
    }
}

這個結構有兩個參數,一個是符號的圖像名稱,另一個是 switch 的顏色。我們會在 makeBody 函數中從頭開始構建 toggle,包括將 text label 放在一側,switch 放在另一側。

configuration 參數為我們提供了兩個信息:toggle 的 text label(即 configuration.label)和狀態(即 configuration.isOn)。在上面的程式碼中,我們使用 HStack 排列 text label 和圓角矩形。另外,我們在圓角矩形上添加一個圓形來構建 switch,並在圓形上面疊加一個圖像視圖來顯示符號。在預設情況下,圖像設置為顯示一個 checkmark。

如我剛剛所說,我們會從頭開始創建一個 switch,因此我們需要處理點擊手勢 (tap gesture),並切換 switch 的狀態。此外,當狀態改變時,我們會改變 offset value 來移動圓形。

我們可以這樣套用 toggleStyle 修飾符,來使用這個客製化 toggle 樣式:

Toggle(isOn: $isEnabled) {
    Text("Airplane mode")
}
.toggleStyle(SymbolToggleStyle(systemImage: "airplane", activeColor: .purple))

以上的程式碼會利用 SymbolToggleStyle 來創建一個 toggle。

建立一個動畫化的圖像 Toggle

swiftui-toggle-light-dark

接下來,讓我們構建另一個 toggle 樣式,它在開啟和關閉的狀態下會顯示不同的圖像。在這個專案中,我們會使用以下這兩個圖像:

下載以上兩張圖像後,把它們匯入到 SwiftUI 專案的 asset catalog 中。然後,我們這樣建立新的 toggle 樣式:

struct ImageToggleStyle: ToggleStyle {

    var onImage = "dark"
    var offImage = "light"

    func makeBody(configuration: Configuration) -> some View {

        HStack {
            configuration.label

            Spacer()

            RoundedRectangle(cornerRadius: 30)
                .fill(configuration.isOn ? .black : Color(.systemGray5))
                .overlay {
                    Image(configuration.isOn ? onImage : offImage)
                        .resizable()
                        .scaledToFill()
                        .clipShape(Circle())
                        .padding(5)
                        .rotationEffect(.degrees(configuration.isOn ? 0 : -360))
                        .offset(x: configuration.isOn ? 10 : -10)
                }
                .frame(width: 50, height: 32)
                .onTapGesture {
                    withAnimation(.spring()) {
                        configuration.isOn.toggle()
                    }
                }
        }
    }
}

ImageToggleStyle 結構有開啟和關閉圖像的兩個參數。實作 switch 的步驟和前文非常相似,只是我們這次不會使用 Circle 視圖來創建 switch,而是使用圖像視圖來顯示開啟和關閉的圖像。

我們也添加了 rotationEffect 修飾符,為 switch 的狀態轉換設置動畫。

同樣地,如果要使用這個 toggle 樣式,我們只需要把 .toggleStyle 修飾符附加到 toggle 視圖即可:

VStack {
    Toggle(isOn: $isEnabled) {
        Text("Light/Dark mode")
            .foregroundColor(isEnabled ? .white : .black)
    }
    .toggleStyle(ImageToggleStyle())
    .padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(isEnabled ? .black.opacity(0.6) : .white)

簡單存取 Toggle 樣式

為了簡化存取客製化 toggle 樣式的步驟,我們可以在 ToggleStyle 的 extension 添加一個狀態變數:

extension ToggleStyle where Self == ImageToggleStyle {

    static var image: ImageToggleStyle { .init() }
}

在這個情況下,我們可以使用 dot syntax 應用 toggle 樣式:

Toggle(isOn: $isEnabled) {
    Text("Light/Dark mode")
        .foregroundColor(isEnabled ? .white : .black)
}
.toggleStyle(.image)

總結

在這篇教學文章中,我們學會了如何在 SwiftUI 創建客製化 Toggle 的樣式。我們可以使用 ToggleStyle 協定,來製作出獨特而漂亮的 Toggle。如果大家想更深入了解 SwiftUI 和 ToggleStyle 協定,可以參閱我們的《精通SwiftUI》一書,當中會有更多技巧和完整的源程式碼。

譯者簡介:Kelly Chan-AppCoda 編輯小姐。
作者
Simon Ng
軟體工程師,AppCoda 創辦人。著有《iOS 17 App 程式設計實戰心法》、《iOS 17 App程式設計進階攻略》以及《精通SwiftUI》。曾任職於HSBC, FedEx等跨國企業,專責軟體開發、系統設計。2012年創立AppCoda技術部落格,定期發表iOS程式教學文章。現時專注發展AppCoda業務,致力於iOS程式教學、產品設計及開發。你可以到推特與我聯絡。
評論
更多來自 AppCoda 中文版
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。