SwiftUI: отклоненное представление вызывает ошибку индекса вне диапазона - PullRequest
0 голосов
/ 09 мая 2020

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

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

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

Если количество ячеек меньше исходного количества ( т.е. 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()
        }
    }
}

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.

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

1 Ответ

0 голосов
/ 09 мая 2020

Хорошо, буквально через несколько минут после создания этой публикации (а я занимаюсь этим пару дней), я думаю, что у меня есть решение. Я изменил свой 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 также решен с помощью вышеуказанной реализации. Может быть, кто-нибудь сможет добавить к этому еще контекст.

...