SwiftUI 常用 Object

剛開始學 SwiftUI,最容易卡住的不是「語法很難」,而是另一個更實際的問題:螢幕上每一塊東西,到底該用哪個物件?

Text、Image 與 Label

1. Text/Image/Label——先把「看得見」的擺出來

用途: 顯示文字、圖片,或「圖+字」綁成一組的標籤。

Text("你好,SwiftUI")
    .font(.title)
Image(systemName: "star.fill")
    .foregroundStyle(.yellow)
Label("收藏", systemImage: "heart.fill")

注意: Image("自己的圖檔名") 要對應專案資源裡的名稱;系統內建圖示請用 systemName:(SF Symbols)。Label 適合列表列、按鈕旁這種「圖文一組」的場合,通常比自己並排 Image+Text 更整齊,也較符合系統慣例。

想改字級、粗細、顏色,多半是接在 Text 後面的修飾器(modifier),例如 .font、.foregroundStyle。先讓字出現,再慢慢調好看,壓力會小很多。

2. Button——讓畫面「做得到」

用途: 使用者點一下,就執行你寫好的動作。

Button("加一") {
    // 點下去要做的事寫在這裡
    print("被點了")
}
.buttonStyle(.borderedProminent)

注意: 按鈕標籤可以是簡單字串,也可以是自訂畫面(例如包一層 Label)。若你希望點了之後畫面上的數字、文字真的改變,通常要配合後面的 @State——只寫 print 時,在 App 預覽裡有時幾乎看不出變化,容易誤以為「按鈕壞了」。

樣式方面,.borderedProminent 適合主要行動;次要操作可用較淡的樣式,避免整頁都在搶注意力。

VStack、HStack 與 ZStack

3. VStack/HStack/ZStack——決定東西怎麼排

用途: 垂直排、水平排,或重疊在一起排。畫面的「骨架」,多半從這三個 Stack 開始。

VStack(spacing: 12) {
    Text("上面")
    Text("下面")
}
HStack {
    Image(systemName: "leaf")
    Text("並排")
}
ZStack {
    Circle().fill(.mint.opacity(0.3))
    Text("疊在圓上")
}

注意: 對齊方式會影響觀感。例如想讓一欄文字靠左,可用 VStack(alignment: .leading) { … }。東西超出螢幕時,記得外層可能需要 ScrollView,否則下方內容會被裁切。

實務小提醒:先決定主軸是直還是橫,再往裡面塞元件。排不好時,不一定是元件錯,常常是 Stack 選錯或對齊沒設。

4. Spacer 與 padding——空白也是設計的一部分

用途: Spacer 吃掉多餘空間、把元件推開;padding 在元件周圍加內距,讓畫面呼吸。

HStack {
    Text("左邊")
    Spacer()
    Text("右邊")
}
.padding()

注意: Spacer 很「餓」,會盡量伸展;佈局突然拉得很開,多半是它在作用。padding() 不帶參數=系統預設邊距;只想改左右,可用 .padding(.horizontal, 16)。

初學常忘:空白不是浪費,而是可讀性。兩行字貼太緊,再漂亮的字級也會顯得擠。

List 與 NavigationStack

5. List 與 ForEach——一列列資料長出來

用途: 做出可捲動列表;資料有多筆時,用 ForEach(或 List 的資料初始化)依陣列重複產生每一列。

struct Item: Identifiable {
    let id = UUID()
    let name: String
}

struct ContentView: View {
    let items = [
        Item(name: "蘋果"),
        Item(name: "香蕉"),
        Item(name: "葡萄")
    ]
    var body: some View {
        List(items) { item in
            Text(item.name)
        }
    }
}

注意: List/ForEach 需要穩定的身份:模型遵守 Identifiable,或明確指定 id:。資料會變動時,請把陣列放進 @State,否則畫面不一定跟著更新。靜態兩三行可用 List { Text("A"); Text("B") };資料一多,再改成「陣列+ForEach」會好維護得多。

6. NavigationStack 與 NavigationLink——頁面之間走來走去

用途: 建立「上一頁/下一頁」的導覽:首頁點進去到詳情,再滑回上一頁。

NavigationStack {
    List {
        NavigationLink("打開詳情") {
            Text("這是第二頁")
                .navigationTitle("詳情")
        }
    }
    .navigationTitle("首頁")
}

注意: .navigationTitle 通常加在導覽堆疊裡的那一頁,不是隨便加在最外層就看得到標題。較舊教學會寫 NavigationView;新專案請優先用 NavigationStack。詳情頁裡再放 Form、List 或另一組 Stack,都沒問題。

若點了沒反應,先檢查:連結是否包在 NavigationStack 裡面。少了這層外殼,NavigationLink 常常「長得像連結,卻走不了」。

SwiftUI @State

7. @State——狀態如何驅動畫面

用途: 記住這個畫面「自己擁有、又會變」的小資料;值一改,依賴它的畫面就重新整理。

struct CounterView: View {
    @State private var count = 0
    var body: some View {
        VStack(spacing: 16) {
            Text("目前:\(count)")
            Button("加一") {
                count += 1
            }
        }
        .padding()
    }
}

注意: @State 適合簡單值——數字、開關、輸入中的字串。資料從父畫面傳進來、又要雙向改,之後會學 @Binding;跨多個畫面共享,再往上接觸更進階的狀態管理。初學只要牢牢抓住這條因果:改 @State → SwiftUI 重畫相關部分。

這也是計數器、待辦勾選、「今晚食什麼」抽籤能動起來的共同核心。看懂它,後面很多範例會突然變親切。

8. TextField/Toggle/Slider——常見輸入控制(簡短版)

用途: 讓使用者輸入文字、撥開關、拉數值——設定頁、表單最常見的三位。

struct ControlsDemo: View {
    @State private var name = ""
    @State private var isOn = true
    @State private var volume = 0.5
    var body: some View {
        Form {
            TextField("你的名字", text: $name)
            Toggle("開啟提醒", isOn: $isOn)
            Slider(value: $volume, in: 0...1)
            Text("音量:\(volume, specifier: "%.2f")")
        }
    }
}

注意: 這類控制幾乎都要 $ 開頭的綁定(Binding),所以旁邊一定有 @State(或由外部傳入的 @Binding)。TextField 的第一個字串是提示(placeholder),不是已經輸入的內容。放進 Form 或 List,系統會幫你排得比較像 iOS 設定頁,少寫很多手動間距。

一張心智地圖,方便之後速查

你想做的事 常先想到
顯示字/圖 Text、Image、Label
點一下做事 Button
排版 VStack、HStack、ZStack、Spacer、padding
很多列資料 List、ForEach
多頁切換 NavigationStack、NavigationLink
畫面要會變 @State
使用者輸入 TextField、Toggle、Slider

真正的小 App,多半是「狀態+排列+一兩個控制」組合起來。零件認齊之後,組裝會愈來愈順。

小練習:十分鐘組一台迷你計數器

打開 Swift Playgrounds,試著做到這三步:

  1. 用 @State 存一個整數
  2. 用 Text 顯示它,用 Button 讓它加一
  3. 有餘力再加一個 Toggle:關掉時隱藏數字,打開再顯示

做完就代表——這本小百科你不是只「看過」,而是「用過」。用過的知識,才會留在手感裡。