Fatal error: Index out of range in SwiftUI
Fatal error: Index out of range in SwiftUI
我制作了一个练习应用程序,其中的主视图是一个简单的列表。当点击列表项时,它会显示详细信息视图。详细视图内有一个“文本字段”,用于更改项目标题。
我执行此步骤时总是出错:
- 向列表添加 3 个项目
- 更改第二项的标题
- 删除第三项
- 删除第二项(您更改标题的那个。
当您删除更改名称的项目时,应用程序会崩溃并向我显示以下错误:“致命错误: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
暗示“当前合法的索引”,它应该来自您观察到的对象。
我制作了一个练习应用程序,其中的主视图是一个简单的列表。当点击列表项时,它会显示详细信息视图。详细视图内有一个“文本字段”,用于更改项目标题。
我执行此步骤时总是出错:
- 向列表添加 3 个项目
- 更改第二项的标题
- 删除第三项
- 删除第二项(您更改标题的那个。
当您删除更改名称的项目时,应用程序会崩溃并向我显示以下错误:“致命错误: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
暗示“当前合法的索引”,它应该来自您观察到的对象。