SwiftUI: индекс вне диапазона при удалении ячеек с помощью переключателя - PullRequest
2 голосов
/ 11 июля 2020

У меня проблемы с удалением ячеек, содержащих Toggle.

Моя модель выглядит так:

class Model: ObservableObject {
    @Published var items: [Item]
    
    init(items: [Item]) {
        self.items = items
    }
}

struct Item: Identifiable {
    var id = UUID()
    var text: String
    var isImportant: Bool
}

И мои взгляды таковы:

struct ContentView: View {
    @EnvironmentObject var model: Model

    var body: some View {
        List {
            ForEach(model.items) {item in
                ItemCell(item: item).environmentObject(self.model)
            }
        .onDelete(perform: deleteItem)
        }
    }
    
    func deleteItem(indexSet: IndexSet) {
        indexSet.forEach({model.items.remove(at: $0)})
    }
}

struct ItemCell: View {
    @EnvironmentObject var model: Model
    var item: Item
    var itemIndex: Int {model.items.firstIndex(where: {$0.id == item.id})!}

    var body: some View {
        Toggle(isOn: $model.items[itemIndex].isImportant) {
            Text(item.text)
        }
    }
}

Как видите, я использую @EnvironmentObject. Каждый раз, когда я пытаюсь удалить ячейку, я получаю это сообщение об ошибке, отображаемое в AppDelegate:

Thread 1: Fatal error: Index out of range

Я предполагаю, что проблема в том, как я передаю данные из моего ContentView() в ItemCell() . Я также попытался интегрировать код ItemCell() в закрытие ForEach, но это не сработало.

Надеюсь, кто-то может мне помочь.

Дополнительный вопрос: что такое цель внедрения с EnvironmentObject (.environmentObject(self.model))? Я не понимаю, когда его использовать, а когда нет. В моем понимании EnvironmentObject - это глобальный объект, который живет в среде и всегда обновляет информацию, независимо от View.

Спасибо! Нико

ОБНОВЛЕНИЕ:

У меня была другая идея, которая тоже не сработала: ContentView:

ForEach(model.items.indices) {index in
  ItemCell(item: self.$model.items[index]).environmentObject(self.model)
}

и ItemCell :

@Binding var item: Item
  var body: some View {
    Toggle(isOn: $item.isImportant) {
      Text(item.text)
  }
}

Есть идеи?

ОБНОВЛЕНИЕ 2

Также это не сработало:

ForEach(Array(model.items.enumerated()), id: \.element) {index, item in
  ItemCell(item: self.$model.items[index]).environmentObject(self.model)
}

1 Ответ

0 голосов
/ 19 июля 2020

Возможное решение - использовать значение по умолчанию вместо принудительного развертывания. Когда вы удаляете строку в списке, ее представление ItemCell может все еще существовать в памяти. Используя значение по умолчанию, вы можете предотвратить сбой (нет риска манипулировать неправильным элементом, поскольку вы больше не можете получить доступ к этому представлению).

var itemIndex: Int { model.items.firstIndex(where: { $0.id == item.id }) ?? 0 }

В качестве альтернативы вы можете использовать дополнительный индекс:

struct ItemCell: View {
    @EnvironmentObject var model: Model
    var item: Item
    var itemIndex: Int? { model.items.firstIndex(where: { $0.id == item.id }) }

    var body: some View {
        Group {
            if itemIndex != nil {
                Toggle(isOn: $model.items[itemIndex!].isImportant) {
                    Text(item.text)
                }
            }
        }
    }
}
...