SwiftUI:删除行时使用 Array/Index 的 ForEach 崩溃
SwiftUI: ForEach using Array/Index crashes when rows are deleted
我看过几篇关于此的帖子,但到目前为止 none 的解决方案似乎对我有用。
我正在尝试使用 ForEach 创建一个可识别项数组——其中包含 Text()
和 Toggle()
视图。该数组存储在 @Published
属性 的 @ObservableObject
.
我目前正在遍历索引以创建切换绑定(如 中所建议)。
似乎一切正常,直到我尝试删除一行。
(特别是最后一行 - 每次都会触发 "Fatal error: Index out of range"。)
如有任何帮助,我们将不胜感激!
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)
}
}
看来你的代码复杂了:
class UserData: ObservableObject {
@Published var rules: [Rule] = []
}
当新元素被添加到 rules 数组时会注意到,你可以通过声明来做到这一点:
@State var rules = [Rule]()
您可能想知道 Rule
class 中的 isEnabled
何时更改。现在它没有发生。对于 ObservableObject
必须是 Rule
class。
记住这一点,如果您将代码更改为:
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)
}
}
}
rules数组加入新元素时会通知,isEnabled
变化时也会通知。
这也解决了您的崩溃问题。
我看过几篇关于此的帖子,但到目前为止 none 的解决方案似乎对我有用。
我正在尝试使用 ForEach 创建一个可识别项数组——其中包含 Text()
和 Toggle()
视图。该数组存储在 @Published
属性 的 @ObservableObject
.
我目前正在遍历索引以创建切换绑定(如
似乎一切正常,直到我尝试删除一行。
(特别是最后一行 - 每次都会触发 "Fatal error: Index out of range"。)
如有任何帮助,我们将不胜感激!
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)
}
}
看来你的代码复杂了:
class UserData: ObservableObject {
@Published var rules: [Rule] = []
}
当新元素被添加到 rules 数组时会注意到,你可以通过声明来做到这一点:
@State var rules = [Rule]()
您可能想知道 Rule
class 中的 isEnabled
何时更改。现在它没有发生。对于 ObservableObject
必须是 Rule
class。
记住这一点,如果您将代码更改为:
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)
}
}
}
rules数组加入新元素时会通知,isEnabled
变化时也会通知。
这也解决了您的崩溃问题。