致命错误:SwiftUI中的索引超出范围

时间:2020-07-16 21:44:39

标签: swift mvvm error-handling swiftui fatal-error

我制作了一个练习应用程序,其中主视图是一个简单列表。轻按列表中的项目后,将显示详细视图。详细信息视图内部是一个“ textField”,用于更改项目标题。

通过执行以下步骤,我总是会出错:

  1. 将3个项目添加到列表中
  2. 更改第二项的标题
  3. 删除第三项
  4. 删除第二项(您更改标题的项。

当您删除更改名称的项目时,应用程序将崩溃并显示以下错误:“致命错误:SwiftUI中的索引超出范围”

我该如何解决?

主视图:

struct ContentView: View {
    @EnvironmentObject var store: CPStore
    
    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(0..<store.items.count, id:\.self) { index in
                        NavigationLink(destination: Detail(index: index)) {
                            VStack {
                                Text(self.store.items[index].title)
                            }
                        }
                    }
                    .onDelete(perform: remove)
                }
                Spacer()
                
                Button(action: {
                    self.add()
                }) {
                    ZStack {
                        Circle()
                        .frame(width: 87, height: 87)
                    }
                }
            }
            .navigationBarTitle("Practice")
            .navigationBarItems(trailing: EditButton())
        }
    }
    
    func remove(at offsets: IndexSet) {
        withAnimation {
            store.items.remove(atOffsets: offsets)
        }
    }
    
    func add() {
        withAnimation {
            store.items.append(CPModel(title: "Item \(store.items.count + 1)"))
        }
    }
}

详细信息视图:

struct Detail: View {
    @EnvironmentObject var store: CPStore
    let index: Int
    
    var body: some View {
        VStack {
            //Error is here
            TextField("Recording", text: $store.items[index].title)
        }
    }
}

模型:

struct CPModel: Identifiable {
    var id = UUID()
    var title: String
}

然后查看模型:

class CPStore: ObservableObject {
    @Published var items = [CPModel]()
}

2 个答案:

答案 0 :(得分:2)

您可以获取foreach中的每个项目,而不是获取数组中的项目数并使用该索引从对象数组中获取项目。在您的内容视图中,将您的For Each更改为此

ForEach(store.items, id:\.self) { item in
                    NavigationLink(destination: Detail(item: item)) {
                        VStack {
                            Text(item.title)
                        }
                    }
                }

并将您的详细信息视图更改为此:

struct Detail: View {
    @EnvironmentObject var store: CPStore
    @State var item: CPModel

    var body: some View {
        VStack {
            //Error is here
            TextField("Recording", text: $item.title)
        }
    }
}

答案 1 :(得分:1)

我的猜测是,当您删除项目索引1时,将触发ContentView触发之前重新渲染索引1的明细。由于SwiftUI不知道index必须首先更新,因为index是一种值类型(与任何东西无关)。

正如另一个答案已经指出的那样,给它一个独立的副本应该可以解决问题。

通常,您应该避免保存索引的副本,因为现在必须始终保持两个事实来源之间的一致性。

就您的使用而言,index表示“当前合法的索引”,该索引应来自观察到的对象。