一锋喜、Protocol ObservableObject
protocol ObservableObject : AnyObject
ObservableObject協(xié)議是SwiftUI和Combine框架中的一種機(jī)制搂鲫,
允許對象在狀態(tài)變化前發(fā)出通知。這一機(jī)制對于構(gòu)建響應(yīng)式用戶界面特別有用潜必,因?yàn)樗_保了當(dāng)數(shù)據(jù)模型更新時(shí)靴姿,UI可以立即做出反應(yīng)并更新自身。
默認(rèn)情況下磁滚,遵循ObservableObject協(xié)議的類會自動(dòng)生成一個(gè)名為objectWillChange的發(fā)布者佛吓,這個(gè)發(fā)布者在任何帶有@Published屬性發(fā)生改變之前發(fā)出信號,通知訂閱者即將發(fā)生的更改垂攘。
class Contact: ObservableObject {
@Published var name: String
@Published var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
func haveBirthday() -> Int {
age += 1
return age
}
}
let john = Contact(name: "John Appleseed", age: 24)
cancellable = john.objectWillChange
.sink { _ in
print("\(john.age) will change")
}
print(john.haveBirthday())
// Prints "24 will change"
// Prints "25"
二维雇、@ObservedObject
@ObservedObject是一個(gè)屬性包裝器類型,它訂閱了一個(gè)可觀察對象晒他,并在可觀察對象發(fā)生變化時(shí)使視圖失效吱型,觸發(fā)視圖的重新渲染。
當(dāng)你在SwiftUI中處理數(shù)據(jù)陨仅,特別是當(dāng)你需要一個(gè)視圖響應(yīng)一個(gè)復(fù)雜對象(即ObservableObject)的屬性變化時(shí)津滞,你會使用@ObservedObject屬性包裝器铝侵。這通常發(fā)生在你需要將一個(gè)StateObject的實(shí)例傳遞給子視圖,并希望子視圖能夠?qū)崟r(shí)響應(yīng)這個(gè)對象狀態(tài)的變化触徐。
以下是一個(gè)具體的例子咪鲜,說明了如何定義一個(gè)數(shù)據(jù)模型作為可觀察對象,然后在主視圖中以StateObject的形式實(shí)例化這個(gè)模型锌介,并將這個(gè)實(shí)例作為ObservedObject傳給子視圖:
class DataModel: ObservableObject {
@Published var name = "Some Name"
@Published var isEnabled = false
}
struct MyView: View {
@StateObject private var model = DataModel()
var body: some View {
Text(model.name)
MySubView(model: model)
}
}
struct MySubView: View {
@ObservedObject var model: DataModel
var body: some View {
Toggle("Enabled", isOn: $model.isEnabled)
}
}
每當(dāng)可觀察對象的任何已發(fā)布(@Published)屬性發(fā)生變化時(shí)嗜诀,SwiftUI會自動(dòng)更新所有依賴于該對象的視圖。子視圖也可以修改模型的屬性孔祸,就像上述例子中的Toggle那樣隆敢,這些更改會傳播到視圖層級中的其他觀察者。
三崔慧、@StateObject
在SwiftUI中拂蝎,使用StateObject作為引用類型在視圖層級中存儲的單一真實(shí)來源。你可以在App惶室、Scene或View中通過將@StateObject屬性應(yīng)用到屬性聲明上温自,并提供一個(gè)遵循ObservableObject協(xié)議的初始值,來創(chuàng)建一個(gè)狀態(tài)對象皇钞。將狀態(tài)對象聲明為私有(private)可以防止從成員級聯(lián)初始化器(memberwise initializer)設(shè)置它們悼泌,因?yàn)檫@可能與SwiftUI提供的存儲管理沖突。
class DataModel: ObservableObject {
@Published var name = "Some Name"
@Published var isEnabled = false
}
struct MyView: View {
@StateObject private var model = DataModel() // Create the state object.
var body: some View {
Text(model.name) // Updates when the data model changes.
MySubView()
.environmentObject(model)
}
}
四夹界、@EnvironmentObject
當(dāng)你在一個(gè)視圖中聲明一個(gè)環(huán)境對象(environment object)時(shí)馆里,意味著這個(gè)視圖及其子視圖將會監(jiān)聽并響應(yīng)任何符合ObservableObject協(xié)議的對象的變化。具體來說可柿,每當(dāng)這個(gè)可觀察對象的屬性發(fā)生變化時(shí)鸠踪,當(dāng)前視圖及其所有依賴于該環(huán)境對象的子視圖都會被標(biāo)記為無效,從而觸發(fā)重新構(gòu)建過程复斥,確保界面能夠反映出最新的狀態(tài)营密。
class UserData: ObservableObject {
@Published var name = "Some Name"
@Published var isEnabled = false
}
struct MyApple: App {
var body: some Scene {
WindowGroup {
ContentView().environmentObject(UserData())
}
}
}
@StateObject var userData:UserData = UserData()
var body: some View {
NavigationView {
List {
Toggle(isOn: $userData.showFavoritesOnly) {
Text("Favorites only")
}
ForEach(userData.landmarks) { landmark in
if !userData.showFavoritesOnly || landmark.isFavorite {
NavigationLink(destination: LandmarkDetai().environmentObject(self.userData)) {
LandmarkRow(landmark: landmark)
}
}
}
}
.navigationBarTitle(Text("Landmarks"))
}
}
}
struct LandmarkDetai: View {
@EnvironmentObject private var userData: UserData
var body: some View {
Button(action: {
print(self.userData.landmarks[1].name)
dismiss()
})
}
}
}
當(dāng)你在一個(gè)視圖中使用.environmentObject(_:)修飾符注冊了一個(gè)環(huán)境對象,這個(gè)對象就會成為該視圖及其所有子視圖環(huán)境的一部分目锭。然后评汰,任何子視圖,無論它在層級結(jié)構(gòu)中的位置有多深痢虹,都可以使用@EnvironmentObject屬性包裝器來訪問這個(gè)對象键俱,就像它是直接嵌入在視圖中的一樣。