我有一个使用视图模型填充的简单列表:
@ObservedObject var sdvm = StepDataViewModel()
[...]
List {
ForEach (vm.steps.indices, id: \.self) { idx in
TheSlider(value: self.$vm.steps[idx].theValue, index: self.vm.steps[idx].theIndex)
}
.onDelete(perform: { indexSet in
self.vm.removeStep(index: indexSet) // <--- here
})
}
viewmodel 在哪里:
class StepDataViewModel: ObservableObject {
@Published var steps: [StepData] = []
func removeStep(index: IndexSet) {
steps.remove(atOffsets: index)
}
}
而 StepData 就是这个:
struct StepData: Equatable, Hashable {
var theIndex: Int
var theValue: Double
}
滑块:
struct TheSlider: View {
@Binding var value: Double
@State var index: Int
var body: some View {
ZStack {
Slider(value: $value, in: 0...180, step: 1)
.padding()
HStack {
Text("[\(index)]")
.font(.body)
.fontWeight(.black)
.offset(y: -20.0)
Text("\(Int(value))")
.offset(y: -20.0)
}
}
}
}
现在,.onDelete
显然已附加到列表中,因此按删除键时我会收到正确的行 index
。
应用程序因索引越界而崩溃的原因是什么?传递给我的列表是否为索引?
我收到此错误:
<块引用>致命错误:索引超出范围:文件 Swift/ContiguousArrayBuffer.swift,第 444 行
会不会是由“TheSlider
”中的直接数组引用引起的?如果是,我如何使用 theValue
作为 Binding
可更新来更改它?
答案 0 :(得分:1)
这是 Deleting list elements from SwiftUI's List 中报告的 SwiftUI 错误。
解决方案是使用来自 here 的扩展来防止访问无效绑定:
struct Safe<T: RandomAccessCollection & MutableCollection, C: View>: View {
typealias BoundElement = Binding<T.Element>
private let binding: BoundElement
private let content: (BoundElement) -> C
init(_ binding: Binding<T>, index: T.Index, @ViewBuilder content: @escaping (BoundElement) -> C) {
self.content = content
self.binding = .init(get: { binding.wrappedValue[index] },
set: { binding.wrappedValue[index] = $0 })
}
var body: some View {
content(binding)
}
}
List {
ForEach(vm.steps.indices, id: \.self) { index in
Safe(self.$vm.steps, index: index) { binding in
TheSlider(value: binding.theValue, index: vm.steps[index].theIndex)
}
}
.onDelete(perform: { indexSet in
self.vm.removeStep(index: indexSet)
})
}
答案 1 :(得分:0)
我相信这是解决 here 相同的问题,即 ForEach 循环中的索引与删除的索引之间的一致性丢失。为索引添加一个单独的数组,并将其用于迭代和删除,如下所示:
@ObservedObject var vm = StepDataViewModel()
@State var indices: [Int] = Array(0..<3)
var body: some View {
List {
ForEach (indices, id: \.self) { idx in
TheSlider(value: self.$vm.steps[idx].theValue, index: self.vm.steps[idx].theIndex)
}
.onDelete(perform: { indexSet in
indices.remove(atOffsets: indexSet) // <--- here
})
}
您将需要一些额外的代码来处理模型中相应项目的删除