问题
I have list of Account
s that each of them has a detail page and they are connected through @Binding
in the AccountDetailView
. Current code work well, updates are fine. No problem at all. However when I added the onDelete
modifier to the ForEach
below and tried swipe to delete
gesture in app, it crashes and says Fatal Error: Index out of range
twice.
I made some search and learned that ForEach
somehow does not get notified -or ignores it, idk much detail- and looks for the last index of the array. In the end, it can not find it and throws the error.
List {
Section(header: Text(String.empty), footer: Text(Strings.SectionFooters.accountListFooter.value)) {
ForEach(self.accountsModel.accounts.indices, id:\.self) { idx in
NavigationLink(destination: AccountDetailView(account: self.$accountsModel.accounts[idx])) {
AccountRow(account: self.$accountsModel.accounts[idx])
}
}.onDelete { (set) in
self.accountsModel.accounts.remove(atOffsets: set)
}
}
}
With keeping id: \.self
parameter in place it throws the error at the AppDelegate
, when I try to remove the parameter, the app works fine but again onDelete
it throws the same error at NavigationLink
's row above.
Here is the AccountDetailView
struct AccountDetailView: View {
@EnvironmentObject var accountsModel: AccountsViewModel
@Binding var account: Account
@State var isEditing: Bool = false
var body: some View {
...
}
}
Finally Account
class conforms to Codable
, NSObject
, Indentifiable
and some other class. I did not want to give all the code just because did not want make the question complicated and hard to examine. If requested, I will provide any part of the code. Thanks in advance.
回答1:
Found a solution. With respect to the this answer to a related question, it came clear. Apperantly, with using the .indices
to fullfill ForEach
makes onDelete
not to work. Instead, going through directly Elements
of an array and creating proxy Bindings
is working.
To be it more clear here are the ContentView
, DetailView
and an extension for Binding
to avoid cluttering view.
ContentView
struct ContentView: View {
@EnvironmentObject var store: DataStore
var body: some View {
NavigationView {
List {
ForEach(self.store.data, id: \.id /*with or without id*/) { (data) in
NavigationLink(destination: DetailView(data: /*created custom initializer*/ Binding(from: data))) {
Text(data.name)
}
}.onDelete { (set) in
self.store.data.remove(atOffsets: set)
}
}.navigationBarTitle("List")
}
}
}
DetailView
struct DetailView: View {
@Binding var data: MyData
var body: some View {
List {
TextField("name", text: self.$data.name)
}
.navigationBarTitle(self.data.name)
.listStyle(GroupedListStyle())
}
}
Binding
extension
extension Binding where Value: MyData {
init(from data: MyData) {
self.init(get: {
data as! Value
}) {
dataStore.data[dataStore.data.firstIndex(of: data)!] = $0
}
}
}
This way, both onDelete
and publishing changes by directly updating the object inside detail view will work.
来源:https://stackoverflow.com/questions/61668606/swiftui-ondelete-throws-fatal-error-index-out-of-range