Ошибка индекса SwiftUI ForEach вне допустимого диапазона при удалении строки
У меня есть ForEach
блок и Stepper
встроен в List
Посмотреть. СодержаниеList
Первый раздел представления выглядит следующим образом:
ForEach(record.nodes.indices, id: \.self) { index in
HStack {
TextField("X", text: self.$record.nodes[index].xString)
Spacer()
Divider()
TextField("Y", text: self.$record.nodes[index].yString)
Spacer()
}
}
Stepper("± node", onIncrement: {
self.record.nodes.append(Node(x: 0, y: 0))
}, onDecrement: {
self.record.nodes.removeLast()
})
Проблема, с которой я столкнулся, заключается в том, что при звонке self.record.nodes.removeLast()
, приложение вылетает с Index out of range
ошибка. Я пытался решить это часами, но безуспешно.
Я изначально использовал onDelete
, однако это вызвало ту же проблему.
Проект можно найти по адресу https://github.com/jacobcxdev/Timekeeper, эта ошибка возникает в RecordDetailView.swift.
3 ответа
описание проблемы
Ошибка индекса вне допустимого диапазона возникает из-за того, что init(_ data: Range<Int>, content: @escaping (Int) -> Content)
инициализатор ForEach
не позволяет нам динамически изменять массив. Для этого нам нужно использоватьinit(_ data: Data, content: @escaping (Data.Element) -> Content)
инициализатор как @Asperi объяснил в комментарии. Однако он не работает и в этой ситуации, когда привязки вложены, как пояснил @JacobCXDev в ответе.
Обходной путь (я нашел лучшее решение, см. Ниже)
Используйте пользовательские привязки и дополнительное состояние. Пользовательские привязки решают проблемуForEach
над вложенными привязками. Кроме того, вам необходимо изменять состояние после каждого прикосновения к настраиваемой привязке, чтобы повторно отображать экран. Ниже приводится упрощенный (непроверенный) образец кода.
@State private var xString: String
ForEach(record.nodes) { node in
let xStringBinding = Binding(
get: { node.xString },
set: {
node.xString = $0
self.xString = $0
}
)
TextField("X", text: xStringBinding)
}
Решение (добавлено 10 июля 2020 г.)
Просто определите структуру представления для детей, как показано ниже.
ForEach(record.nodes) { node in
NodeView(node: node)
}
struct NodeView: View {
@ObservedObject var node: Node
var body: some View {
TextField("X", text: self.$node.xString)
}
}
Он работает как следующие коды.
struct Node {
var xString: String = "x"
var yString: String = "y"
var x: Int
var y: Int
}
struct RecordsNode {
var nodes : [Node]
}
struct ContentView: View {
@State var record: RecordsNode = RecordsNode(nodes: [Node(x: 11, y: 11)])
var body: some View {
Group{
ForEach(record.nodes.indices, id: \.self) { index in
HStack {
TextField("X", text: self.$record.nodes[index].xString)
Spacer()
Divider()
TextField("Y", text: self.$record.nodes[index].yString)
Spacer()
}
}
Stepper("± node", onIncrement: {
self.record.nodes.append(Node(x: 0, y: 0))
}, onDecrement: {
self.record.nodes.removeLast()
})}
}}
У меня была та же проблема, которую я решил, добавив условие if перед ForEach.
if (!IsDeleting) {
...
.OnDecrement{
IsDeleting := true
}
}
какое-нибудь лучшее решение?