SwiftUI Nested ForEach вызывает фатальную ошибку: каждый элемент макета может возникнуть только один раз
Я пытаюсь создать представление LazyVGrid для отображения содержимого объектов в массиве с помощью вложенных операторов ForEach. Код вызывает сбой приложения с сообщением "Неустранимая ошибка: каждый элемент макета может возникнуть только один раз".
Каждый объект содержит массив значений, которые необходимо отобразить в виде строки в сетке. Строка состоит из ячейки со строкой артикула объекта, за которой следует количество ячеек с целыми числами из массива объекта.
Я решил использовать класс вместо структуры, потому что мне нужно обновить массив внутри класса.
Это класс для объектов в массиве.
class RoomPickupData: Identifiable {
let id = UUID()
var sku: String = ""
// Array of counts for sku on a particular date
var roomsForDate: [Date: Int] = [:]
}
В коде первый ForEach используется для размещения информации заголовка в первой строке сетки.
Следующий ForEach и вложенный в него ForEach вызывают ошибку.
Внешний ForEach выполняет итерацию по массиву объектов, поэтому я могу создать строку в сетке для каждого объекта. Каждая строка состоит из строки, за которой следуют значения из массива roomsForDate. Размер массива roomsForDate не фиксирован.
Если я закомментирую вложенный ForEach, код будет выполнен, но с ним я получу фатальную ошибку.
Если я закомментирую Text(roomData.value.description) так, чтобы во внутреннем ForEach было отмечено, код тоже работает нормально. Если я заменю Text(roomData.value.description) на Text(""), приложение выйдет из строя.
Вложенность ForEach кажется лучшим способом создания представления сетки из двухмерного массива или массива объектов, каждый из которых содержит массив.
Я нашел другие сообщения, показывающие, что вложенные операторы ForEach можно использовать в SwiftUI.
Вот код. Ваша помощь в решении этой проблемы приветствуется.
struct RoomTableView: View {
@ObservedObject var checkfrontVM: CheckFrontVM
let dateFormatter = DateFormatter()
init(checkfrontVM: CheckFrontVM) {
self.checkfrontVM = checkfrontVM
dateFormatter.dateFormat = "MM/dd/yyyy"
}
func initColumns() -> [GridItem] {
var columns: [GridItem]
var columnCount = 0
if self.checkfrontVM.roomPickupDataArray.first != nil {
columnCount = self.checkfrontVM.roomPickupDataArray.first!.roomsForDate.count
} else {
return []
}
columns = [GridItem(.flexible(minimum: 100))] + Array(repeating: .init(.flexible(minimum: 100)), count: columnCount)
return columns
}
var body: some View {
if self.checkfrontVM.roomPickupDataArray.first != nil {
ScrollView([.horizontal, .vertical]) {
LazyVGrid(columns: initColumns()) {
Text("Blank")
ForEach(self.checkfrontVM.roomPickupDataArray.first!.roomsForDate.sorted(by: {
$0 < $1
}), id: \.key) { roomData in
Text(dateFormatter.string(from: roomData.key))
}
ForEach(self.checkfrontVM.roomPickupDataArray) { roomPickupData in
Group {
Text(roomPickupData.sku)
.frame(width: 80, alignment: .leading)
.background(roomTypeColor[roomPickupData.sku])
.border(/*@START_MENU_TOKEN@*/Color.black/*@END_MENU_TOKEN@*/)
ForEach(roomPickupData.roomsForDate.sorted(by: { $0.key < $1.key}), id: \.key) { roomData in
Text(roomData.value.description)
}
}
}
}
.frame(height: 600, alignment: .topLeading)
}
.padding(.all, 10)
}
}
}
2 ответа
Каждому элементу в ForEach нужен уникальный идентификатор. Код можно изменить, добавив следующую строку в текстовое представление во внутреннем ForEach, чтобы присвоить уникальный идентификатор.
.id ("тело (roomPickupData.id)-(roomData.key)")
ForEach(self.checkfrontVM.roomPickupDataArray) { roomPickupData in
Group {
Text(roomPickupData.sku)
.frame(width: 80, alignment: .leading)
.background(roomTypeColor[roomPickupData.sku])
.border(Color.black)
ForEach(roomPickupData.roomsForDate.sorted(by: { $0.key < $1.key}), id: \.key) { roomData in
Text(roomData.value.description)
.id("body\(roomPickupData.id)-\(roomData.key)") //<-
}
}
}
Я столкнулся с той же ошибкой внутри
ForEach
внутри
LazyVStack
Я решил это, добавив
UUID()
идентификатор самого внутреннего View, например
.id(UUID)
Вот пример: LazyVStack {
ForEach(vmHome.allCategories, id: \.self) { category in
VStack {
HStack {
Text(category)
.font(.title3)
.bold()
.padding(10)
Spacer()
}
ScrollView(.horizontal) {
LazyHStack{
ForEach(vmHome.getMovies(forCat: category), id: \.self.id) {
movie in
StandardHomeMovieView(movie: movie)
.id(UUID())
}
}
}
}
}