![](/img/trans.png)
[英]SwiftUI - @Published property is not updating view from coordinator
[英]SwiftUI @Published property not updating views
在下面的代碼中,點擊 FavoriteButton 時詳細視圖 UI 不會更新。 我們知道綁定已連接到 ObservableObject,因為調用了didSet
並打印了foos
數組的更新后的 state。 奇怪的是,我發現在FooList
的NavigationView
中添加VStack
可以解決此實例中的錯誤,但對於遇到此問題的更復雜的 UI 卻沒有。
我是否缺少有關如何連接的信息? 另一種解決方法是在FavoriteButton
中切換State
布爾值,但這似乎違背了綁定的觀點。
import SwiftUI
struct Foo: Identifiable {
var id = UUID()
var name: String
var isFavorite: Bool
}
class FooData: ObservableObject {
@Published var foos = [Foo(name: "Test", isFavorite: true)] {
didSet {
print(foos)
}
}
}
struct FooList: View {
@ObservedObject var fooData = FooData()
var body: some View {
NavigationView {
// Adding a VStack makes the FavoriteButton update correctly, but this doesn't work for more complicated UIs
// VStack {
List {
ForEach(fooData.foos, id: \.id) { foo in
let index = fooData.foos.firstIndex(where: { $0.id == foo.id })!
NavigationLink(foo.name, destination: FooDetailView(foo: $fooData.foos[index]))
}
}
// }
}
}
}
struct FooDetailView: View {
@Binding var foo: Foo
var body: some View {
FavoriteButton(isFavorite: $foo.isFavorite)
}
}
struct FavoriteButton: View {
@Binding var isFavorite: Bool
var body: some View {
Button(action: {
isFavorite.toggle()
}, label: {
if isFavorite {
Image(systemName: "heart.fill")
} else {
Image(systemName: "heart")
}
})
}
}
FooData
作為EnvironmentObject
傳遞給List
並在FavoriteButton
中聲明EnvironmentObject
可以解決此問題。 這必須足以告訴 SwiftUI 該視圖關心對FooData
的任何更新,即使沒有代碼直接引用EnvironmentObject
。 這感覺有點像魔術,對我來說絕對不是最直觀的行為。 希望這可以幫助其他人。
完整的工作代碼:
import SwiftUI
struct Foo: Identifiable {
var id = UUID()
var name: String
var isFavorite: Bool
}
class FooData: ObservableObject {
@Published var foos = [Foo(name: "Test", isFavorite: true)] {
didSet {
print(foos)
}
}
}
struct FooList: View {
@ObservedObject var fooData = FooData()
var body: some View {
NavigationView {
List {
ForEach(fooData.foos, id: \.id) { foo in
let index = fooData.foos.firstIndex(where: { $0.id == foo.id })!
NavigationLink(foo.name, destination: FooDetailView(foo: $fooData.foos[index]))
}
}
}
.environmentObject(fooData)
}
}
struct FooDetailView: View {
@Binding var foo: Foo
var body: some View {
FavoriteButton(isFavorite: $foo.isFavorite)
}
}
struct FavoriteButton: View {
@EnvironmentObject var fooData: FooData
@Binding var isFavorite: Bool
var body: some View {
Button(action: {
isFavorite.toggle()
}, label: {
if isFavorite {
Image(systemName: "heart.fill")
} else {
Image(systemName: "heart")
}
})
}
}
當您有一個 viewModel 發布一個數組時,唯一可以觸發視圖更新的是數組計數。 因此,您需要將數組中的對象class
,將其擴展為ObservableObject
,並將isFavorite
@Published var
。
class Foo: Identifiable, ObservableObject {
var id = UUID()
var name: String
@Published var isFavorite: Bool
init(name: String, isFavorite: Bool) {
self.name = name
self.isFavorite = isFavorite
}
}
此外,您應該將您的FooData
@StateObject
,因為該視圖擁有它。
struct ContentView: View {
@StateObject var fooData = FooData()
var body: some View {
NavigationView {
List {
ForEach(fooData.foos) { foo in
NavigationLink(foo.name, destination: FooDetailView(foo: foo))
}
}
}
}
}
將foo
作為@ObservedObject
向下傳遞
struct FooDetailView: View {
@ObservedObject var foo: Foo
var body: some View {
Button(action: {
foo.isFavorite.toggle()
}, label: {
if foo.isFavorite {
Image(systemName: "heart.fill")
} else {
Image(systemName: "heart")
}
})
}
}
您可以使用仍然可以訪問ObservableObject
的NavigationLink
視圖的中間“包裝器”來解決此問題:
struct FooList: View {
@ObservedObject var fooData = FooData()
var body: some View {
NavigationView {
List {
ForEach(fooData.foos, id: \.id) { foo in
NavigationLink(foo.name, destination: FooWrapper(id: foo.id, fooData: fooData))
}
}
}
}
}
struct FooWrapper : View {
var id : UUID
@ObservedObject var fooData : FooData
var fooBinding : Binding<Foo> {
.init { () -> Foo in
let index = fooData.foos.firstIndex(where: { $0.id == id })!
return fooData.foos[index]
} set: { (newValue) in
let index = fooData.foos.firstIndex(where: { $0.id == id })!
fooData.foos[index] = newValue
}
}
var body: some View {
FooDetailView(foo: fooBinding)
}
}
我重用了您的代碼來查找foo
的index
——就個人而言,我可能會編寫一個解決方案,而不會在發生崩潰時強制展開,但這是一個與問題不完全相關的問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.