问题
I've seen several posts about this, but so far none of the solutions seem to be working for me.
I'm trying to create an array of Identifiable items using ForEach -- with both a Text()
and Toggle()
view inside. The array is stored in a @Published
property of an @ObservableObject
.
I'm currently looping through the indices to create the toggle bindings (as suggested in other posts).
Everything appears to be working, until I try to delete a row.
(Specifically the last row - which triggers a "Fatal error: Index out of range" every time.)
Any help would be greatly appreciated!
struct Rule: Identifiable {
let id: String
var displayName: String
var isEnabled: Bool
}
class UserData: ObservableObject {
@Published var rules: [Rule] = []
}
struct RuleListView: View {
@ObservableObject var userData: UserData
var body: some View {
List {
ForEach(userData.rules.indices, id: \.self) { index in
HStack {
Toggle(
isOn: self.$userData.rules[index].isEnabled
) { Text("Enabled") }
Text(self.userData.rules[index].displayName)
}
}
.onDelete(perform: delete)
}
}
func delete(at offsets: IndexSet) {
userData.rules.remove(atOffsets: offsets)
}
}
回答1:
It seems you have complicated your code:
class UserData: ObservableObject {
@Published var rules: [Rule] = []
}
Will notice when new element is added to rules array, you could have done that just by declaring:
@State var rules = [Rule]()
You probably want to know when isEnabled
in Rule
class changes. Right now it is not happening. For that to ObservableObject
must comform the Rule
class.
Keeping that in mind, if you change your code to:
import SwiftUI
class Rule: ObservableObject, Identifiable {
let id: String
var displayName: String
@Published var isEnabled: Bool
init(id: String, displayName: String, isEnabled: Bool) {
self.id = id
self.displayName = displayName
self.isEnabled = isEnabled
}
}
struct ContentView: View {
// for demonstration purpose, you may just declare an empty array here
@State var rules: [Rule] = [
Rule(id: "0", displayName: "a", isEnabled: true),
Rule(id: "1", displayName: "b", isEnabled: true),
Rule(id: "2", displayName: "c", isEnabled: true)
]
var body: some View {
VStack {
List {
ForEach(rules) { rule in
Row(rule: rule)
}
.onDelete(perform: delete)
}
}
}
func delete(at offsets: IndexSet) {
rules.remove(atOffsets: offsets)
}
}
struct Row: View {
@ObservedObject var rule: Rule
var body: some View {
HStack {
Toggle(isOn: self.$rule.isEnabled)
{ Text("Enabled") }
Text(rule.displayName)
.foregroundColor(rule.isEnabled ? Color.green : Color.red)
}
}
}
It will notice when new element is added to rules array, and also will notice when isEnabled
changes.
This also solves your problem with crashing.
来源:https://stackoverflow.com/questions/59335797/swiftui-foreach-using-array-index-crashes-when-rows-are-deleted