我制作了一个练习应用程序,其中主视图是一个简单列表。轻按列表中的项目后,将显示详细视图。详细信息视图内部是一个“ textField”,用于更改项目标题。
通过执行以下步骤,我总是会出错:
当您删除更改名称的项目时,应用程序将崩溃并显示以下错误:“致命错误: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]()
}
答案 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
表示“当前合法的索引”,该索引应来自观察到的对象。