SwiftUI:删除行时使用 Array/Index 的 ForEach 崩溃

Posted

技术标签:

【中文标题】SwiftUI:删除行时使用 Array/Index 的 ForEach 崩溃【英文标题】:SwiftUI: ForEach using Array/Index crashes when rows are deleted 【发布时间】:2019-12-14 13:58:33 【问题描述】:

我已经看过几篇关于此的帖子,但到目前为止,似乎没有一个解决方案适合我。

我正在尝试使用 ForEach 创建一个可识别项目数组——其中包含 Text()Toggle() 视图。该数组存储在@ObservableObject@Published 属性中。

我目前正在遍历索引以创建切换绑定(如other posts 中所建议的那样)。

一切似乎都在工作,直到我尝试删除一行。

(特别是最后一行 - 每次都会触发“致命错误:索引超出范围”。)

任何帮助将不胜感激!

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)
  


【问题讨论】:

medium.com/better-programming/… 【参考方案1】:

看来你的代码很复杂:

class UserData: ObservableObject 
  @Published var rules: [Rule] = []

当新元素添加到规则数组时会注意到,你可以通过声明来做到这一点:

@State var rules = [Rule]()

您可能想知道 Rule 类中的 isEnabled 何时更改。现在它没有发生。对于 ObservableObject 必须是 Rule 类。

记住这一点,如果您将代码更改为:

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)
    
  

新元素添加到规则数组时会通知,isEnabled 更改时也会通知。 这也解决了您的崩溃问题。

【讨论】:

以上是关于SwiftUI:删除行时使用 Array/Index 的 ForEach 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

在 UIKit 中我们可以设置 label.numberOfLines = 0 当我们不知道文本将占用多少行时?在 SwiftUI 中有啥替代方法? [复制]

使用 CASCADE 删除行时显示警告

使用 CoreData 删除行时 UITableView 崩溃

使用 Rcpp 删除矩阵行时出错

为啥删除 UITableView 行时会出现错误?

Xcode Playground 中的 SwiftUI 列表