SwiftUI @Binding не обновляет вид

У меня есть простой интерфейс master/detail, где детальный вид изменяет элемент в массиве. Используя ниже, модель обновляется должным образом, но 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
    }
}

Главное представление, которое отображает список 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")
        }
    }
}

Подробный вид, позволяющий изменить стоимость инвентаря предмета:

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)
        }
    }
}

Нажатие на степпер в представлении ItemDetail изменяет элемент в магазине, но текст степпера не изменяется. Возвращение к списку подтверждает, что модель была изменена. Также я подтвердил, что магазин звонит willChange.send() его издателю. Я бы предположил, что send() вызов обновляет ItemStore в среде и подробном представлении @Binding свойство должно быть уведомлено об изменении и обновить отображение (но это не так).

Я попытался изменить свойство item ItemDetail для использования @State:

@State var item: ProduceItem = ProduceItem(name: "Plums", inventory: 7)

В этом случае модель элемента обновляется при использовании степпера, а представление обновляется, отображая обновленный инвентарь. Может кто-нибудь объяснить, почему с помощью @Binding свойство не обновляет интерфейс, но локально @State собственность делает?

1 ответ

Решение

Здесь у вас есть обходной путь. Используйте индекс вместо элемента при вызове 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)
        }
    }
}
Другие вопросы по тегам