SwiftUI @Binding 不刷新视图

smr*_*smr 10 swiftui combine

我有一个简单的主/细节界面,其中细节视图修改数组中的一个项目。使用以下内容,模型已正确更新,但 SwiftUI 不会刷新视图以反映更改。

模型:

struct ProduceItem: Identifiable {
    let id = UUID()
    let name: String
    var inventory: Int
}

final class ItemStore: BindableObject {
    var willChange = PassthroughSubject<Void, Never>()

    var items: [ProduceItem] { willSet { willChange.send() } }

    init(_ items: [ProduceItem]) {
        self.items = items
    }
}
Run Code Online (Sandbox Code Playgroud)

显示 ProduceItems 列表的主视图(一个 ItemStore 被插入到 SceneDelegate 的环境中):

struct ItemList: View {
    @EnvironmentObject var itemStore: ItemStore

    var body: some View {
        NavigationView {
            List(itemStore.items.indices) { index in
                NavigationLink(destination: ItemDetail(item: self.$itemStore.items[index])) {
                    VStack(alignment: .leading) {
                        Text(self.itemStore.items[index].name)
                        Text("\(self.itemStore.items[index].inventory)")
                            .font(.caption)
                            .foregroundColor(.secondary)
                    }
                }
            }
            .navigationBarTitle("Items")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

可让您更改项目库存值的详细信息视图:

struct ItemDetail: View {
    @Binding var item: ProduceItem

    var body: some View {
        NavigationView {
            Stepper(value: $item.inventory) {
                Text("Inventory is \(item.inventory)")
            }
            .padding()
            .navigationBarTitle(item.name)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在 ItemDetail 视图中点击步进器会修改商店中的项目,但步进器的文本不会改变。导航回列表确认模型已更改。此外,我确认商店致电willChange.send()其出版商。我会假设send()调用会更新环境中的 ItemStore,并且@Binding应该将更改通知详细视图的属性并刷新显示(但事实并非如此)。

我尝试更改 ItemDetail 的 item 属性以使用@State

@State var item: ProduceItem = ProduceItem(name: "Plums", inventory: 7)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,模型是在使用步进器时更新项目并刷新视图,显示更新的库存。谁能解释为什么使用该@Binding属性不会刷新界面,但本地@State属性会?

kon*_*iki 5

在这里,您有一个解决方法。调用 ItemDetail 时使用索引而不是元素。在 ItemDetail 中,您使用@EnvironmentObject.

struct ItemList: View {
    @EnvironmentObject var itemStore: ItemStore

    var body: some View {
        NavigationView {
            List(itemStore.items.indices) { index in
                NavigationLink(destination: ItemDetail(idx: index)) {
                    VStack(alignment: .leading) {
                        Text(self.itemStore.items[index].name)
                        Text("\(self.itemStore.items[index].inventory)")
                            .font(.caption)
                            .foregroundColor(.secondary)
                    }
                }
            }
            .navigationBarTitle("Items")
        }
    }
}

struct ItemDetail: View {
    @EnvironmentObject var itemStore: ItemStore
    let idx: Int


    var body: some View {
        NavigationView {
            Stepper(value: $itemStore.items[idx].inventory) {
                Text("Inventory is \(self.itemStore.items[idx].inventory)")
            }
            .padding()
            .navigationBarTitle(itemStore.items[idx].name)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)