文章目錄
- 引言
- 一、`@State`
- 1.1 基本概念
- 1.2 初始化與默認值
- 1.3 注意事項
- 二、`@Binding`
- 2.1 基本概念
- 2.2 初始化與使用
- 2.3 注意事項
- 三、`@ObservedObject`
- 3.1 基本概念
- 3.2 初始化與使用
- 3.3 注意事項
- 四、`@EnvironmentObject`
- 4.1 基本概念
- 4.2 初始化與使用
- 4.3 注意事項
- 五、`@StateObject`
- 5.1 基本概念
- 5.2 初始化與使用
- 5.3 注意事項
- 六、@ObservedObject、@StateObject、@EnvironmentObject區別及使用場景
- 6.1 區別
- 6.1.1 對象創建和所有權
- 6.1.2 生命周期管理
- 6.1.3 數據傳遞方式
- 6.2 使用場景
- 6.2.1 `@ObservedObject`
- 6.2.2 `@StateObject`
- 6.2.3 `@EnvironmentObject`
- 七、綜合案例
- 7.1 電商購物案例
- 7.2 代碼解釋
- 7.2.1 數據模型
- 7.2.2 購物車視圖模型(`ShoppingCartViewModel`)
- 7.2.3 商品單元格視圖(`ProductCell`)
- 7.2.4 商品列表視圖(`ProductListView`)
- 7.2.5 購物車視圖(`CartView`)
- 7.2.6 主視圖(`MainView`)
- 八、小結
引言
在 SwiftUI 中,狀態管理是構建交互式和動態用戶界面的核心。狀態代表著應用程序的數據,當這些數據發生變化時,SwiftUI 會自動更新與之關聯的視圖,以反映最新的狀態。本文將詳細介紹 SwiftUI 中幾種常見的狀態管理方式,包括 @State
、@Binding
、@ObservedObject
、@EnvironmentObject
和 @StateObject
,并探討它們的使用場景、初始化、默認值設置以及注意事項。
一、@State
1.1 基本概念
@State
是 SwiftUI 中用于管理視圖私有狀態的屬性包裝器。它通常用于存儲簡單的值,如布爾值、整數、字符串等,并且只能在結構體視圖中使用。當 @State
變量的值發生變化時,SwiftUI 會重新計算并更新依賴于該變量的視圖部分。
1.2 初始化與默認值
@State
變量必須在聲明時進行初始化,因為它代表著視圖的初始狀態。可以為其提供一個默認值,這個默認值將作為視圖首次顯示時的狀態。
import SwiftUIstruct StateExampleView: View {// 初始化 @State 變量并設置默認值@State private var isFavorite = falsevar body: some View {Button(action: {self.isFavorite.toggle()}) {Text(isFavorite ? "已收藏" : "收藏")}}
}
在上述代碼中,isFavorite
是一個 @State
變量,初始值為 false
。當按鈕被點擊時,isFavorite
的值會取反,視圖會相應地更新顯示內容。
1.3 注意事項
- 私有性:
@State
變量應該是私有的,因為它是視圖的內部狀態,不應該被外部視圖直接訪問或修改。 - 值類型:
@State
通常用于存儲值類型(如結構體、枚舉),因為值類型的賦值會創建一個新的副本,這有助于 SwiftUI 檢測狀態的變化。 - 視圖重建:當
@State
變量的值發生變化時,SwiftUI 會重新計算整個視圖的body
屬性,因此應避免在body
中執行昂貴的操作。
二、@Binding
2.1 基本概念
@Binding
用于在不同視圖之間共享狀態,實現雙向數據綁定。它允許一個視圖修改另一個視圖的狀態,通常用于將父視圖的 @State
變量傳遞給子視圖。
2.2 初始化與使用
@Binding
變量不能直接初始化,它必須通過外部傳遞的 Binding
實例進行賦值。通常在父視圖中使用 $
符號將 @State
變量轉換為 Binding
實例,并傳遞給子視圖。
import SwiftUI// 子視圖
struct TextFieldView: View {@Binding var text: Stringvar body: some View {TextField("輸入文本", text: $text)}
}// 父視圖
struct BindingExampleView: View {@State private var inputText = ""var body: some View {VStack {// 將 @State 變量轉換為 Binding 并傳遞給子視圖TextFieldView(text: $inputText)Text("你輸入的文本是: \(inputText)")}}
}
在上述代碼中,TextFieldView
接收一個 @Binding
變量 text
,并將其綁定到 TextField
上。父視圖 BindingExampleView
將自己的 @State
變量 inputText
通過 $
符號轉換為 Binding
實例傳遞給子視圖。當用戶在 TextField
中輸入文本時,父視圖中的 inputText
會相應更新。
2.3 注意事項
- 依賴外部狀態:
@Binding
變量依賴于外部傳遞的Binding
實例,因此必須確保在使用之前已經正確初始化。 - 數據一致性:由于
@Binding
實現了雙向數據綁定,任何對@Binding
變量的修改都會反映到原始的@State
變量上,需要注意數據的一致性和正確性。
三、@ObservedObject
3.1 基本概念
@ObservedObject
用于觀察符合 ObservableObject
協議的對象。當被觀察對象的 @Published
屬性發生變化時,SwiftUI 會自動更新關聯的視圖。@ObservedObject
通常用于管理復雜的狀態邏輯,將狀態和業務邏輯封裝在一個獨立的對象中。
3.2 初始化與使用
@ObservedObject
變量可以在視圖中直接初始化,也可以通過外部傳遞。被觀察的對象必須符合 ObservableObject
協議,并且需要使用 @Published
標記需要觀察的屬性。
import SwiftUI
import Combine// 定義一個符合 ObservableObject 協議的類
class CounterViewModel: ObservableObject {// 使用 @Published 標記需要觀察的屬性@Published var count = 0func increment() {count += 1}
}struct ObservedObjectExampleView: View {// 初始化 @ObservedObject 變量@ObservedObject private var viewModel = CounterViewModel()var body: some View {VStack {Text("計數: \(viewModel.count)")Button(action: {self.viewModel.increment()}) {Text("增加計數")}}}
}
在上述代碼中,CounterViewModel
是一個符合 ObservableObject
協議的類,包含一個 @Published
屬性 count
。ObservedObjectExampleView
使用 @ObservedObject
觀察 CounterViewModel
的實例。當點擊按鈕調用 viewModel.increment()
方法時,count
屬性的值會改變,SwiftUI 會自動更新 Text
視圖以顯示新的計數。
3.3 注意事項
- 對象生命周期:
@ObservedObject
不會管理被觀察對象的生命周期,因此需要確保對象在視圖使用期間不會被銷毀。通常在父視圖中創建對象并傳遞給子視圖,或者使用@StateObject
來管理對象的生命周期。 - 線程安全:
@Published
屬性的修改應該在主線程上進行,因為 SwiftUI 的視圖更新是在主線程上執行的。如果在后臺線程中修改@Published
屬性,可能會導致視圖更新不一致或崩潰。
四、@EnvironmentObject
4.1 基本概念
@EnvironmentObject
用于在整個視圖層次結構中共享一個 ObservableObject
實例。與 @ObservedObject
不同的是,@EnvironmentObject
可以在多個視圖中輕松訪問同一個狀態對象,而不需要通過層層傳遞參數。
4.2 初始化與使用
@EnvironmentObject
變量不需要在視圖中初始化,它會從視圖環境中獲取共享的 ObservableObject
實例。在父視圖中,需要使用 environmentObject
修飾符將 ObservableObject
實例注入到視圖環境中。
import SwiftUI
import Combine