iOS 開發 #11 | SwiftUI ( 1 )
SwiftUI 是什麼
前言
在開發時發現 SwiftUI 這個好玩的東西,接觸了一段時間後該把這段時間學的東西記錄下來
契機
在一開始學習開發 iOS 時是使用 storyboard 來設計 UI 介面,但在設定 Auto Layout 時一直出現紅色的警告,看了警告訊息也看不出哪邊有問題,且即使在 storyboard 設計完 UI 介面後還得拉線拉到 ViewController 裡面,拉著拉著在 ViewController 的 Code 就越來越多,那你說不想拉線不也可以用 Code 來產生 UI 元件嗎,沒錯是可以用 Code 來產生 UI 元件,但設計個畫面得打多少 Code 啊,如果我懶癌發作又把元件跟控制的 Code 都寫在一起那畫面會有多混亂我可不敢想
想一想在 iOS 中有沒有像 Flutter 、Jetpack Compose 那種聲明式的框架?ㄟ恭喜在 iOS 中還真有這種框架,那就是 SwiftUI
SwftUI
SwiftUI 是一種聲明式框架。聲明式風格的核心思想是描述界面的最終狀態,然後讓框架負責在狀態發生變化時自動更新 UI
- 聲明式語法:
開發者定義 UI 的最終狀態,而不是描述如何一步步構建 UI
UI 元素和其狀態緊密結合,當狀態改變時,UI 自動更新 - 數據驅動:
使用狀態管理(如@State
、@Binding
、@ObservedObject
等)來自動刷新 UI - 可組合性:
UI 元素是可以組合的,構建複雜界面時可以將小的 UI 組件組合成大的 UI 組件
import SwiftUI
struct ContentView: View {
@State private var counter = 0
var body: some View {
// HomePage()
VStack {
Text("Counter: \(counter)")
.padding()
Button(action: {
counter += 1
}) {
Text("Increment")
}
}
}
}
#Preview {
ContentView()
}
這邊用計數器來做介紹
struct ContentView: View
ContentView
這個結構遵守了 View
協定,這意味著它是 SwiftUI
中的一個 View
這是 SwiftUI 的核心概念之一:每個視圖都是一個結構,且必須遵守
View
協定
var body: some View
body
屬性會返回某種類型的 View
,但具體是哪一種類型可以隱藏起來
這利用了 Swift 的 opaque return types 概念,即它告訴編譯器
body
會返回一個遵循View
協定的視圖,但不必明確指定返回的具體視圖類型
var body: some View
如何運作
當 SwiftUI 需要顯示 ContentView
的時候,它會檢查 body
屬性,並渲染其中描述的視圖結構。在這個例子中,body
的值是一個 VStack
,裡面有一個 Text
和一個 Button
,這些視圖組合起來形成了 ContentView
的界面
介紹完 struct ContentView: View
& var body: some View
後,接著來介紹裡面的 UI 元件
Text
這是 SwiftUI 中顯示文字的 View
Text("這邊放你要顯示的文字")
常用的幾個參數 :
.font(_:)
:設定字體大小和風格.foregroundStyle(_:)
:設定文字顏色.padding(_:)
:設定內邊距.background(_:)
:設定背景顏色.bold()
:加粗文字.italic()
:文字斜體.lineLimit(_:)
:設定行數限制.multilineTextAlignment(_:)
:設定多行文字對齊方式
Button
這個相當於 UIButton
,UIButton
是什麼應該不用我介紹吧……嗎?
Button(action: {
print("Button tapped")
}) {
Text("Tap Me")
.padding() // 設定內邊距
.background(Color.blue) // 設定背景顏色
.foregroundColor(.white) // 設定文字顏色
.cornerRadius(8) // 設定圓角
}
action
是用來設定按鈕的點擊事件,而 {}
裡面是用來設定按鈕的 View
,例如這個例子是一個圓角半徑為 8 、背景為藍色、裡面的字體顏色為白色的按鈕,按了會 print
Button tapped
在 Button
中,我的理解是 Button
是賦予子物件 Button
的特性,如 action
,而 label
或 {}
裡面的 view
是設定這個 button
的畫面,所以在上面的例子中那些 Modifier
其實是在設定 Text
的樣式
如果要做出像 Android 或 Flutter 的 Card
的樣式,可以用 VStack
、HStack
、ZStack
搭配 Modifier
來設計成 Card
幾個常用的 Modifier
.padding(_:)
:設定內邊距.background(_:)
:設定背景顏色.cornerRadius(_:)
:設定圓角.shadow(color:radius:x:y:)
:設定陰影
.cornerRadius(_:)
已被棄用,如果要設定圓角可以使用.clipShape(RoundedRectangle(cornerRadius: CGFloat))
介紹完比較簡單的元件後,來介紹跟排版有關的元件
這邊會把五種跟排版有關的元件都介紹一次
VStack
VStack
用來垂直排列子物件
VStack(alignment: .center, spacing: 10) {
Text("First")
Text("Second")
Text("Third")
}
常用參數
alignment
:子視圖的水平對齊方式。可選值有.leading
、.center
、.trailing
等spacing
:子視圖之間的垂直間距
HStack
HStack
用於水平排列子視圖
HStack(alignment: .center, spacing: 10) {
Text("First")
Text("Second")
Text("Third")
}
常用參數
alignment
:子視圖的垂直對齊方式。可選值有.top
、.center
、.bottom
等spacing
:子視圖之間的水平間距
ZStack
ZStack
用於將子視圖堆疊在一起,從後到前顯示
ZStack(alignment: .center) {
Color.red
Text("Hello")
Text("World")
}
在這邊我放了兩個
Text
,這樣比較好知道這個元件所做的事
常用參數
alignment
:子視圖的對齊方式。可選值有.top
、.bottom
、.center
、.leading
、.trailing
等
GeometryReader
GeometryReader
用於獲取父視圖的幾何信息,允許動態調整子視圖的大小和位置
GeometryReader { geometry in
VStack {
Text("Width: \(geometry.size.width)")
Text("Height: \(geometry.size.height)")
}
}
Spacer
Spacer
用於在容器內創建可變空間,通常用於推動其他視圖
HStack {
Text("Left")
Spacer()
Text("Right")
}
這邊可以看到 Left
跟 Right
被 Spacer
給擠到螢幕兩側
在上面的介紹中能發現在 VStack
中只能設定 .leading
、.center
、.trailing
這三種水平對齊,而 HStack
中只能設定 .top
、.center
、.bottom
這三種垂直對齊
由於在 SwiftUI 的 UI 編排方式是用堆疊的方式, VStack
會將子視圖從上到下進行堆疊,並且 alignment
參數決定了每個子視圖在水平軸上的對齊方式。而 HStack
會將子視圖從左到右進行堆疊,並且 alignment
參數決定了每個子視圖在垂直軸上的對齊方式
如果想要在 VStack
中控制子視圖的對齊方式,並且希望有更細緻的控制,可以考慮使用其他容器視圖,如 HStack
或 GeometryReader
,這樣可以更靈活地進行布局和對齊。如果想要將視圖推到上面或下面,可以使用 Spacer
。在 SwiftUI 中,對於需要更複雜的對齊和布局,可以使用 Spacer
和其他容器視圖來達到需求
那介紹了元件後,我們回頭來看看上面提到的計數器
import SwiftUI
struct ContentView: View {
@State private var counter = 0
var body: some View {
// HomePage()
VStack {
Text("Counter: \(counter)")
.padding()
Button(action: {
counter += 1
}) {
Text("Increment")
}
}
}
}
#Preview {
ContentView()
}
會發現 Code 中有一個 @State
@State
是 SwiftUI 中最基本的狀態管理屬性包裝器。當 @State
標記的值發生變化時,SwiftUI 會自動重新計算依賴這個狀態的視圖,並刷新它們
當按下按鈕時,counter
值變化,SwiftUI 會自動重新計算並刷新顯示 counter
的 Text
視圖
後記
這個 SwiftUI
系列會介紹我在使用 SwiftUI
開發時所發現的事物或在開發時遇到的問題,如果我的懶癌沒發作且有發現新東西的話這系列應該會連載吧……嗎?