SwiftUI: отклоненное представление вызывает ошибку индекса вне диапазона

Этот рвал мне волосы, надеюсь, кто-нибудь покажет мне, в чем я ошибаюсь.

Цель состоит в том, что мне нужно иметь возможность управлять динамическим списком элементов (т.е. список может увеличиваться или уменьшаться), где свойства каждого элемента могут быть скорректированы (отредактированы), а элементы связаны с данным родителем.

В сокращенном коде ниже я назвал элемент Cell, который принадлежит Row. Строка может иметь много ячеек, и каждая ячейка имеет количество, которое может быть изменено пользователем. Это сокращенная версия моего кода, которая, я надеюсь, делает маркировку отношений немного более ясной, но, что особенно важно, она дает мне ту же ошибку, которая:

Если количество ячеек меньше исходного количества (т.е. 3 или меньше в этом примере), тогда приложение вылетает с ошибкой «Индекс вне диапазона» всякий раз, когда закрывается представление. До этого момента ячейки можно было добавлять или удалять без каких-либо проблем, и я не получаю эту ошибку при изменении количества элементов ячеек. Я просмотрел все SO и различные блоги и не могу найти никого, кто сталкивался с этой конкретной проблемой - кажется, что большинство сообщений Index вне диапазона происходит при фактическом изменении списка, моя ошибка возникает только после того, как список сокращается и затем отклонен.

Я прикрепил пример кода ниже - вы сможете вырезать / вставить и попробовать.

PS. Я знаю, что calculateTotals () - это скетч; пожалуйста, не добавляйте буквы или знаки препинания к сумме - это просто для проверки корректности привязки :)

Ячейка

struct Cell: Identifiable {
    var amount: String
    var id = UUID()

    init(_ amount: String = "0.00"){
        self.amount = amount
    }
}

Строка

class Row: ObservableObject {
    @Published var cells: [Cell]

    init(){
        self.cells = [
            Cell("10"),
            Cell("15"),
            Cell("20"),
            Cell("25")]
    }
}

ContentView

struct ContentView: View {
    @State private var displayAmounts = false

    var body: some View {
        NavigationView {
            Button(action: {
                self.displayAmounts.toggle()
            }) {
                Text("View amounts")
            }
        }
        .sheet(isPresented: self.$displayAmounts) {
            CellSheet()
        }
    }
}

Таблица ячеек

struct CellSheet: View {
    @ObservedObject var row: Row = Row()

    private func deleteCell(at offsets: IndexSet) {
        self.row.cells.remove(atOffsets: offsets)
    }

    private func calculateTotals() -> String {
        var total = Double("0.00")!

        for cell in self.row.cells {
            if( "" != cell.amount ) {
                total += Double(cell.amount)!
            }
        }

        return String("\(total)")
    }

    var body: some View {
        VStack{
            List {
                ForEach(row.cells.indices, id: \.self){ i in
                    CellItem(amount: self.$row.cells[i].amount)
                }.onDelete(perform: deleteCell)
            }
            Button(action: {
                self.row.cells.append(Cell())
            }) {
                Text("Add new cell")
            }

            Text(calculateTotals())
        }
    }
}

CellItem - здесь какая-то странность: если я заключу TextField в HStack, приложение сразу выйдет из строя при удалении ячейки - даже если общее количество ячеек больше исходного количества (4).

struct CellItem: View {
    @Binding var amount: String

    var body: some View {
        // Uncomment HStack and deleting rows immediately causes index out of range.
//        HStack {
            TextField("Amount: ", text: $amount)
//        }
    }
}

Я действительно не понимаю, почему и как это происходит. Очевидно, Swift пытается получить доступ к несуществующему индексу (если я удалю привязку и просто выведу значение, проблем не возникнет), но я не понимаю, почему это может вызвать проблемы при закрытии представления. Я предполагаю, что Swift кое-что кеширует в памяти? Проблема с упаковкой HStack также является своеобразной.

В любом случае, я относительно новичок в Swift, поэтому, возможно, я упускаю из виду что-то очевидное. Для дополнительного контекста я использую XCode 11.4.1 и ориентируюсь на iOS 13.4.

Вы сможете перенести весь этот код прямо в новый проект, и он будет скомпилирован. Любая помощь будет с благодарностью получена :)


person ComputerSaysNo    schedule 09.05.2020    source источник


Ответы (1)


Хорошо, буквально через несколько минут после создания этого поста (а я занимался этим пару дней), я думаю, что у меня есть решение. Я изменил свой List () в ContentView следующим образом:

List {
    ForEach(Array(row.cells.enumerated()), id:\.1.id) { (i, cell) in
        CellItem(amount: self.$row.cells[i].amount)
    }.onDelete(perform: deleteCell)
}

Это было основано на этом ответе, поскольку я думаю (?) Enumerated () лучше подходит, когда массив имеет переменную длину . Сбой HStack также решен с помощью вышеупомянутой реализации. Может быть, кто-нибудь сможет добавить к этому еще немного контекста.

person ComputerSaysNo    schedule 09.05.2020
comment
Понятно, что сбой возвращается, когда это последний элемент в массиве. - person ComputerSaysNo; 09.05.2020