Индексы не обновляются при удалении элемента из списка

#swiftui

Вопрос:

Я хотел бы создать представление, в которое я могу передать массив, и заставить представление редактировать массив. Следующий код является упрощенным примером:

 struct Item: Identifiable {
  var name: String
  var id = UUID()
}

struct EditItems: View {
  @Binding var item_list: [Item]
  
  var body: some View {
    List {
      ForEach(item_list.indices) { idx in
        Text(item_list[idx].name)
      }
      .onDelete(perform: deleteItem)
    }
    .toolbar {
      ToolbarItem(placement: .principal) {
        EditButton()
      }
    }
  }
  
  func deleteItem(at offsets: IndexSet) {
    item_list.remove(atOffsets: offsets)
  }
}
 

Это компилируется и запускается изначально. Я могу нажать «Редактировать» и удалить элемент списка. После удаления элемента списка, когда я нажимаю «Готово», я получаю «Фатальная ошибка: индекс вне диапазона». Отладчик сообщает мне, что в моем списке 7 элементов, но строка Text(item_list[idx].name) пытается выполнить с idx = 7 помощью .

Таким образом, похоже, что после удаления элемента ForEach он все еще работает со старыми индексами вместо новых более коротких индексов. Это потому item_list , что это не @State так ? Когда я попытался сделать @State и @Binding то, и другое, и получил кучу ошибок.

Как мне это исправить?

Ответ №1:

Инициализатор для ForEach этого принимает диапазон, который может использоваться только для постоянных данных.

Из документов Apple:

Экземпляр считывает только начальное значение предоставленных данных, и ему не нужно идентифицировать представления в обновлениях.

Используйте один из других ForEach инициализаторов, например:

 ForEach(item_list.enumerated(), id: .self) { idx, element in
 

Комментарии:

1. Ну, я буду, я никогда не замечал, что некоторые из них постоянны, а некоторые нет. Спасибо!

Ответ №2:

Вы использовали конструктор, из ForEach которого создается постоянный контейнер, используйте другой, с явными идентификаторами, например

 List {
  ForEach(item_list.indices, id: .self) { idx in    // << here !!
    Text(item_list[idx].name)
  }