SwiftUI:关闭视图导致索引超出范围错误

Posted

技术标签:

【中文标题】SwiftUI:关闭视图导致索引超出范围错误【英文标题】:SwiftUI: Dismissed view causes Index out of Range error 【发布时间】:2020-05-09 10:07:39 【问题描述】:

这个一直在扯我的头发,我希望有人能告诉我我哪里出错了。

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

在下面的节略代码中,我将项称为 Cell,它属于 Row。一个 Row 可以有许多 Cells,每个 Cell 都有一个可以由用户更改的数量。这是我的代码的删节版本,我希望它能让关系标签更清晰一些,但至关重要的是,它给了我同样的错误:

如果单元格的数量小于原始数量(即本示例中为 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")]
    

内容视图

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 - 这里有些奇怪:如果我用 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】:

好的,在创建这篇文章几分钟后(我已经写了几天了),我想我有一个解决方案。我在 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 崩溃问题。也许有人可以为此添加更多上下文。

【讨论】:

实现了我的希望 - 当它是数组中的最后一项时崩溃返回。

以上是关于SwiftUI:关闭视图导致索引超出范围错误的主要内容,如果未能解决你的问题,请参考以下文章

从数组中删除 - 致命错误:索引超出范围 - SwiftUI 绑定

索引超出范围的 SwiftUI 问题

索引超出范围 SwiftUI

Swiftui foreach 索引超出范围 ondelete 问题

SwiftUI - 索引超出范围

SwiftUI .onDelete 抛出致命错误:索引超出范围