工作表在 SwiftUI 中显示多次

mih*_*ema 3 swift swiftui swiftui-list swiftui-sheet

简短描述:在详细视图中,我有一个相关实体的列表。对于每个项目,都有一个按钮可打开该项目的编辑表。

List {
  if (book.booksBorrowers != nil) {
    ForEach (Array(book.booksBorrowers! as! Set<Borrowers>), id: \.self) { borrower in
      HStack {
        Text(borrower.firstName ?? "unbekannter Vorname")
        Text(borrower.lastName ?? "unbekannter Nachname")
        Text(String(format: "%.0f", borrower.age))
        Spacer()
        Button {
          showingBorrowerEditScreen.toggle()
        } label: {
          Image(systemName: "pencil")
            .frame(width: 20.0, height: 20.0)
        }.multilineTextAlignment(.center).buttonStyle(.borderless)
          .sheet(isPresented: $showingBorrowerEditScreen) {
          EditBorrowerView(
            aBorrower: borrower,
            firstName: borrower.firstName!,
            lastName: borrower.lastName!,
            age: Int(borrower.age)
            
          ).environment(\.managedObjectContext, self.viewContext)
        }
      }
    }.onDelete(perform: deleteBorrower)
  }
}.listStyle(.inset(alternatesRowBackgrounds: true))
Run Code Online (Sandbox Code Playgroud)

单击列表中的某个编辑按钮后,会出现一张带有编辑表单的工作表,其中预填充了所选列表项的值。

struct EditBorrowerView: View {
  @Environment(\.managedObjectContext) var moc
  @Environment(\.dismiss) var dismiss
  
  @State private var firstName = ""
  @State private var lastName = ""
  @State private var age = 0.0
  
  @StateObject var aBorrower: Borrowers

  init(aBorrower: Borrowers, firstName: String, lastName: String, age: Int) {
    self._aBorrower = StateObject(wrappedValue: aBorrower)
    self._firstName = State(initialValue: aBorrower.firstName ?? "")
    self._lastName = State(initialValue: aBorrower.lastName ?? "")
    self._age = State(initialValue: Double(aBorrower.age))
  }
  let formatter: NumberFormatter = {
    let formatter = NumberFormatter()
    formatter.numberStyle = .decimal
    formatter.minimumFractionDigits = 0
    formatter.maximumFractionDigits = 0
    return formatter
  }()

  var body: some View {
    VStack {
      Text("Ausleiher bearbeiten").font(.title)
      Form {
        VStack {
          TextField("Vorname", text: $firstName)
          TextField("Nachname", text: $lastName)
          HStack {
            Slider(value: $age, in: 0...99, step: 1)
            TextField("Alter", value: $age, formatter: formatter)
          }
        }
      }
      HStack {
        Button("Save") {
          // save the edited book
          aBorrower.firstName = firstName
          aBorrower.lastName = lastName
          aBorrower.age = Double(age)
          
          try? moc.save()
          dismiss()
        }
        Button("Cancel") {
          dismiss()
        }
      }
    }.padding(10)
  }
}
Run Code Online (Sandbox Code Playgroud)

但是现在,当单击一行的编辑按钮时

  • a) 显示表单中仅显示第一个列表项的内容
  • b) 单击工作表中的“取消”,列表中的每个项目在消失之前都会出现工作表,其中包含相应的值。

使用 print() 进行的小型调试显示,单击编辑按钮时,首先设置正确的值(例如,我单击第三项,传递第三项的值),但此外,所有列表项也传递到工作表。


第一张图片作为上问题的示例

第二张图片作为上问题的示例

Sco*_*man 6

如果视图中有一个.sheet(isPresented:)在循环中重复的修改器,但布尔状态变量位于循环之外,则该状态变量将用于工作表的多个副本。有时,这可能会表现为一张工作表,其中显示的详细信息与您单击的行不同;有时您可能会看到多张纸。听起来您所看到的副作用与此有关。

有几种方法可以解决这个问题。

选项1 -sheet(item:)

将您替换showingBorrowerEditScreenborrowerToEdit可选对象的状态变量,默认为 nil:

@State private var borrowerToEdit: Borrower? = nil
Run Code Online (Sandbox Code Playgroud)

在按钮操作中,将其设置为循环中当前行的借用者:

Button {
  borrowerToEdit = borrower
} label: {
  // etc
Run Code Online (Sandbox Code Playgroud)

最后,在循环item:使用sheet修饰符的形式。请注意,此形式采用一个块,其中包含对相关借用者对象的引用。ForEach

ForEach(...) { borrower in
  // etc.
}
.sheet(item: $borrowerToEdit) { borrower in 
  EditBorrowerView(
    aBorrower: borrower,
    firstName: borrower.firstName!,
    lastName: borrower.lastName!,
    age: Int(borrower.age)            
  )
}
Run Code Online (Sandbox Code Playgroud)

选项 2 - 单独的子视图

如果您想坚持使用布尔值来表示是否应使用模式表,则需要通过提取行详细信息将该布尔值隔离到其自己的子视图。例如:

List {
  if (book.booksBorrowers != nil) {
    ForEach (Array(book.booksBorrowers! as! Set<Borrowers>), id: \.self) { borrower in
      BorrowerListRow(borrower: borrower)
      .onDelete(perform: deleteBorrower)
    }
  }
}

struct BorrowerListRow: View {
  @ObservedObject var borrower: Borrower
  @State private var showingBorrowerEditScreen = false

  var body: some View {
    HStack {
      Text(borrower.firstName ?? "unbekannter Vorname")
      Text(borrower.lastName ?? "unbekannter Nachname")
      Text(String(format: "%.0f", borrower.age))
      Spacer()
      Button {
        showingBorrowerEditScreen.toggle()
      } label: {
        Image(systemName: "pencil")
          .frame(width: 20.0, height: 20.0)
      }.multilineTextAlignment(.center).buttonStyle(.borderless)
        .sheet(isPresented: $showingBorrowerEditScreen) {
          EditBorrowerView(
            aBorrower: borrower,
            firstName: borrower.firstName!,
            lastName: borrower.lastName!,
            age: Int(borrower.age)
          ).environment(\.managedObjectContext, self.viewContext)
        }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这样,每一行都有自己的“我是否应该显示模式”布尔值,因此不会混淆哪一行“拥有”活动工作表,并且一次只能显示一个。


您选择哪个选项部分取决于个人喜好。我倾向于在我自己的代码中选择选项 1,因为感觉模式更多地是由列表而不是一行“拥有”的,并且它强化了您一次只能编辑一个借款人的想法。但无论哪种方法都应该消除您当前遇到的问题。