SwiftUI Form 表單製作教學

Form 是 SwiftUI 中專門用來製作「資料輸入表單」或「設定頁面」的容器。它會自動套用系統標準的群組樣式 (Grouped Style),讓你的 App 瞬間擁有原生的 iOS 質感。

基礎 Form

只要把所有輸入元件放進 Form 裡,SwiftUI 就會自動幫你排版。它會讓每個控制項佔滿一行,並且自動處理好邊距。

struct SettingsView: View {
    @State private var username = ""
    @State private var isPrivate = true
    @State private var notificationEnabled = false
    
    var body: some View {
        Form {
            TextField("使用者名稱", text: $username)
            Toggle("私人帳號", isOn: $isPrivate)
            Toggle("開啟通知", isOn: $notificationEnabled)
            
            Button("登出") {
                // logout
            }
            .foregroundColor(.red)
        }
    }
}

Section 分組與說明

Form 最強大的功能是使用 Section 來將內容邏輯分組。你可以加上 header (分組標題) 和 footer (底部說明文字)。

Form {
    Section(header: Text("個人資料")) {
        TextField("暱稱", text: $username)
        DatePicker("生日", selection: .constant(Date()))
    }
    
    Section(header: Text("隱私設定"), footer: Text("開啟後,只有朋友看得到你的動態。")) {
        Toggle("私人帳號", isOn: $isPrivate)
        
        // 條件顯示:只有當開啟私人帳號時,才顯示進階選項
        if isPrivate {
            Toggle("允許陌生人搜尋", isOn: .constant(false))
        }
    }
    
    Section {
        Button("刪除帳號", role: .destructive) { }
    }
}

隱藏/顯示 Section

由於 SwiftUI 的宣告式特性,你可以輕易地用 if 來控制整塊 Section 是否出現。

if showAdvancedSettings {
    Section("進階設定") {
        // ...
    }
}

Form 中的 Picker

當你在 Form 中使用 Picker 時,它預設會變成「點擊後跳轉到新頁面選擇」的樣式 (Navigation Link Style),這是 iOS 設定頁面常見的行為。此時你必須確保 Form 被包在 NavigationStack 中。

NavigationStack {
    Form {
        Picker("通知頻率", selection: $frequency) {
            Text("每天").tag(0)
            Text("每週").tag(1)
            Text("每月").tag(2)
        }
        // 如果想要更改樣式,可以使用 pickerStyle
        .pickerStyle(.inline) // 變成展開的單選清單
    }
    .navigationTitle("設定")
}

表單驗證與停用按鈕

在表單中,我們通常希望在使用者填寫完整前,禁止按下送出按鈕。這可以透過 .disabled() Modifier 達成。

struct SignupForm: View {
    @State private var email = ""
    @State private var password = ""
    
    // 計算屬性:判斷是否有效
    var isFormValid: Bool {
        !email.isEmpty && password.count >= 8
    }
    
    var body: some View {
        Form {
            TextField("Email", text: $email)
            SecureField("密碼 (至少8碼)", text: $password)
            
            Section {
                Button("註冊") {
                    print("註冊中...")
                }
                .disabled(!isFormValid) // 如果無效則禁用
            }
        }
    }
}

List vs Form 的選擇

你可能會發現 ListForm 看起來很像。

  • Form: 專為「輸入資料」設計,會根據作業系統 (iOS/iPadOS/macOS) 自動調整控制項的樣式。例如 Toggle 在 Form 裡是標準開關,DatePicker 會變成一行顯示。
  • List: 專為「顯示大量資料」設計,效能較好,適合用來顯示動態陣列。

黃金法則

  • 如果你是在做「設定頁面」或「註冊表單」 (靜態內容為主) 👉 使用 Form。
  • 如果你是在做「商品列表」或「留言板」 (動態陣列為主) 👉 使用 List