SwiftUI List .onDelete:索引超出范围
Posted
技术标签:
【中文标题】SwiftUI List .onDelete:索引超出范围【英文标题】:SwiftUI List .onDelete: Index out of range 【发布时间】:2021-03-19 17:03:45 【问题描述】:我有一个使用视图模型填充的简单列表:
@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
)
视图模型在哪里:
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
可更新?
【问题讨论】:
为什么需要Hashable和Identifiable?如果你要使用\.self,那么你只需要Hashable。 这是崩溃的原因吗? 一开始我需要尽可能多地清理代码,所以我问。 我从问题中删除了Identifiable
,当然崩溃也是一样的。
让我控制吧,这不难
【参考方案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)
)
【讨论】:
我不知道您是如何获得这个“安全”解决方案的,但它运作良好。我需要学习很多。谢谢!【参考方案2】:我相信这与 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
)
您将需要一些额外的代码来处理模型中相应项目的删除
【讨论】:
以上是关于SwiftUI List .onDelete:索引超出范围的主要内容,如果未能解决你的问题,请参考以下文章
Swiftui foreach 索引超出范围 ondelete 问题
macOS 上的 SwiftUI:如何为 onDelete 启用 UI(从列表中删除)