在下面的代码中,当点击“收藏夹”按钮时,详细信息视图 UI 不会更新。我们知道绑定连接到 ObservableObject,因为 didSet
被调用并打印 foos
数组的更新状态。奇怪的是,我发现在 VStack
的 FooList
中添加 NavigationView
可以解决此实例中的错误,但对于我遇到此问题的更复杂的 UI 却没有。
我是否遗漏了有关如何连接的信息?另一种解决方法是在 State
中切换 FavoriteButton
布尔值,但这似乎与绑定背道而驰。
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
并在 EnvironmentObject
中声明 FavoriteButton
可以解决该问题。这必须足以告诉 SwiftUI 此 View 关心 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")
}
})
}
}
答案 0 :(得分:1)
您可以使用仍然可以访问 NavigationLink
的 ObservableObject
视图的中间“包装器”解决此问题:
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)
}
}
我重用了您的代码来查找 index
的 foo
-- 就我个人而言,我可能会编写一个在发生崩溃时不强制展开的解决方案,但这是一个不完全相关的问题问题。
答案 1 :(得分:0)
当您有一个 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")
}
})
}
}