Reputation: 197
I am trying to get my head around SwiftUI bindings. Here I display an array in a view and bind the values to the second view. In the second view I remove the data from the array.
However I get the following,
Fatal error: Index out of range
I am not getting an error for self.person.notes.remove(at: self.index)
in fact this is actually removing the note in the array. It must be in the first view when using ForEach
as the array has been modified and now it is out of bounds. But I am unsure how to get around this ? Surely the Binding
should have resolved this.
View 1
ForEach(self.person.notes.indices, id:\.self) { index in
NoteView(person: self.$person, room: self.$home.notes[index], index: index)
}
View 2
@Binding var person: Person
@Binding var note: Note
var index: Int
if self.index > 0 {
Button(action: {
self.person.notes.remove(at: self.index)
}) {
Text("Remove")
}
}
Any idea how this is supposed to work in SwiftUI ?
Upvotes: 6
Views: 1966
Reputation: 1860
Removing with a pre given index is always a risk. Possibility of data changing in background etc. Get the index at the time you try to remove the item to be sure to have the right one. In case the data changed somewhere else. This should help you understand:
struct ContentView: View {
@State var myData: Array<String> = ["first", "second", "third"]
var body: some View {
ForEach(self.myData, id: \.self) { data in
SecondView(myData: self.$myData, data: data)
}
}
}
struct SecondView: View {
@Binding var myData: Array<String>
// the data that is being tied to the view
var data: String
var body: some View {
Button(action: {
// calculate the index at the time of removal
if let index = self.myData.firstIndex(of: self.data) {
self.myData.remove(at: index)
}
}) {
Rectangle().foregroundColor(Color.red).frame(width: 100, height: 100).overlay(Text("Button \(self.data)"))
}
}
}
Produces this:
As you mentioned your error lies in the first view. This is most likely because you are building the ForEach with the number of indices. The moment you remove one, the ForEach runs into the error as it loses "track" of the item and the item with the index the ForEach provided does not exist anymore. Try this:
ForEach(self.person.notes, id:\.self) { note in
// Remove the index and calculate it on the second view as shown above. This then should solve the problem
NoteView(person: self.$person, room: self.$home.notes)
}
Upvotes: 2