Fatal error: Index out of range in SwiftUI

Fatal error: Index out of range in SwiftUI

我制作了一个练习应用程序,其中的主视图是一个简单的列表。当点击列表项时,它会显示详细信息视图。详细视图内有一个“文本字段”,用于更改项目标题。

我执行此步骤时总是出错:

  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]()
}

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

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 的详细信息会在触发 ContentView 之前触发重新呈现。由于 SwiftUI 不知道 index 必须首先更新,因为 index 是一个值类型(独立于任何东西)。

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

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

在您的用法中,index 暗示“当前合法的索引”,它应该来自您观察到的对象。