ObservableObject
和 @ObservedObject
ObservableObject
是一個protocol
,要求實現協議的類型為class
,有一個需要實作的屬性objectWillChange
。- 當數據將要發生改變時,這個屬性用來向外進行「廣播」,它的訂閱者(一般是 View 相關的邏輯)在收到通知後,對 View 進行刷新。
- 創建遵從
ObservableObject
協議的類別後,實際在 View 裡使用需要將此物件宣告為@ObservedObject
。
範例:點擊按鈕擲骰子
使用
PassthroughSubject
中send()
方法通知事件將要發生。PassthroughSubject
於Combine
框架中,將於後續介紹。
ContentView
中model
為reference type
,使用@ObservedObject
將它和ContentView
關聯起來。當model
中的屬性diceState
將要改變,objectWillChange
就會發出事件,使得body
被調用進行UI刷新。
struct ContentView: View {
@ObservedObject private var model = DiceModel()
var body: some View {
CounterButton(diceState: $model.diceState)
}
}
class DiceModel: ObservableObject {
let objectWillChange = PassthroughSubject<Void, Never>()
var diceState: DiceState = .one {
willSet {
objectWillChange.send()
}
}
}
enum DiceState: Int {
case one = 1
case two = 2
case three = 3
case four = 4
case five = 5
case six = 6
}
@Published
- 在
ObservableObject
中,每個對介面可能產生影響的屬性都可以如diceState
的willSet
那樣,手動調用objectWillChange.send()
。若 model 有很多屬性逐一添加willSet
很麻煩且重複。 - 簡化寫法為:省略宣告
objectWillChange
,並將屬性標記為Published
。
class DiceModel: ObservableObject {
@Published var diceState: DiceState = .one
}
@EnvironmentObject
- 當多個
View
需要訪問到同一個model
,使用@ObservedObject
需要大量的屬性傳遞,改用@EnvironmentObject
可以便捷的達到大批量共享。 - 跟單例很像,只要在
View
層級以上,不論在哪裡都可以訪問到這個環境對象。
struct ContentView: View {
@EnvironmentObject var model: DiceModel
var body: some View {
CounterButton()
}
}
struct CounterButton: View {
@EnvironmentObject var model: DiceModel
var body: some View {
Button {
let value = Int.random(in: 1...6)
let dice = DiceState(rawValue: value)
model.diceState = dice ?? .one
} label: {
Circle()
.frame(width: 150, height: 150, alignment: .center)
.foregroundColor(.blue)
.overlay {
Text("\(model.diceState.rawValue)")
.font(.system(size:72, weight: .bold))
.foregroundColor(.white)
}
}
}
}
在創建 ContentView
的地方注入 environmentObject
@main
struct SwiftUIDemoApp: App {
var body: some Scene {
WindowGroup {
ContentView().environmentObject(DiceModel())
}
}
}
總結
@State
和@Binding
提供 View 內部的狀態儲存,應是簡單的 value type 標記為private
僅供內部使用。ObservableObject
中@ObservedObject
和@EnvironmentObject
則是針對跨越 View 層級的狀態共享,可以處理較複雜的數據類型,為 reference type。需要在數據變化時向外發送通知,來觸發介面刷新。- 建議一開始可以先選擇使用
@ObservedObject
,若發現狀態可以被限制在同一個 View 層級,則改用@State
;若狀態需要大量共享,則改用@EnvironmentObject