SwiftUI:取消视图导致索引超出范围错误

时间:2020-05-09 10:07:39

标签: swiftui swiftui-list

这个人一直在扯我的头发,我希望有人可以告诉我我要去哪里。

目标是我需要能够管理项目的动态列表(即,列表可以增长或缩小),其中可以调整(编辑)每个项目的属性,并且这些项目与给定的父项相关。 / p>

在下面的简化代码中,我将其称为“单元格”,它属于“行”。一个行可以有多个单元格,每个单元格都有一个可由用户更改的数量。这是我的代码的简化版本,希望它使关系标签更清晰一些,但至关重要的是,它给了我相同的错误,即:

如果“单元格”的数量小于原始数量(在此示例中为3个或更少),则无论何时关闭视图,应用程序都会崩溃并显示“索引超出范围”错误。至此,可以添加或删除单元格而没有任何问题,并且在更改单元格项目的数量时我会收到此错误。我到处都是SO和各种博客,找不到任何遇到过此特定问题的人-似乎大多数Index out of Range帖子都在实际修改列表时发生,我的错误仅在列表缩小后才会发生,并且然后被解雇。

我在下面附加了一些示例代码-您应该可以剪切/粘贴并尝试一下。

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 -这里有些奇怪:如果我用HStack包裹TextField,则删除单元格后应用立即崩溃-即使单元格总数大于原始数量(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 :(得分:0)

好吧,在写这篇文章的几分钟后(我已经讨论了几天),我想我有一个解决方案。我已经在ContentView中修改了List(),如下所示:

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

这是基于this answer的,因为我认为(?)enumerated()更适合数组长度可变的情况。上面的实现也解决了HStack崩溃的问题。也许有人可以为此添加更多上下文。