我在SwiftUI中遇到了这个问题。我希望当用户按下按钮时能够从数组中删除一个项目,但我得到一个";线程1:致命错误:索引超出范围";尝试时出错。这似乎与IntView接受@Binding这一事实有关:如果我将num
作为一个正则变量,那么代码就可以正常工作,不会出错。不幸的是,出于我的目的,我需要能够将绑定传递到视图(这是一个简化的情况(,所以我不确定我需要做什么,这样绑定就不会导致错误。
这是我的代码:
import SwiftUI
struct IntView: View {
@Binding var num: Int // if I make this "var num: Int", there are no bugs
var body: some View {
Text("(num)")
}
}
struct ArrayBugView: View {
@State var array = Array(0...10)
var body: some View {
ForEach(array.indices, id: .self) { num in
IntView(num: $array[num])
Button(action: {
self.array.remove(at: num)
}, label: {
Text("remove")
})
}
}
}
非常感谢您的帮助!
在您的代码中,带有indicies
和id: .self
的ForEach
是一个错误。SwiftUI中的ForEach
视图不像传统的for循环。ForEach
的文件规定:
/// It's important that the `id` of a data element doesn't change, unless
/// SwiftUI considers the data element to have been replaced with a new data
/// element that has a new identity.
这意味着我们不能在ForEach
中使用索引、枚举或新数组。ForEach
必须位于可识别项目的实际数组上。这就是为什么SwiftUI可以跟踪四处移动的行视图,这被称为结构身份,你可以在2021年的《解密SwiftUI WWDC》中了解它。
所以你必须把你的代码改成这样的东西:
import SwiftUI
struct Item: Identifiable {
let id = UUID()
var num: Int
}
struct IntView: View {
let num: Int
var body: some View {
Text("(num)")
}
}
struct ArrayView: View {
@State var array: [Item] = [Item(num:0), Item(num:1), Item(num:2)]
var body: some View {
ForEach(array) { item in
IntView(num: item.num)
Button(action: {
if let index = array.firstIndex(where: { $0.id == item.id }) {
array.remoteAt(index)
}
}, label: {
Text("remove")
})
}
}
}