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