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

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 適合主要行動;次要操作可用較淡的樣式,避免整頁都在搶注意力。

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)。
初學常忘:空白不是浪費,而是可讀性。兩行字貼太緊,再漂亮的字級也會顯得擠。

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 常常「長得像連結,卻走不了」。

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,試著做到這三步:
- 用
@State存一個整數 - 用 Text 顯示它,用 Button 讓它加一
- 有餘力再加一個 Toggle:關掉時隱藏數字,打開再顯示
做完就代表——這本小百科你不是只「看過」,而是「用過」。用過的知識,才會留在手感裡。
