Лист отображается несколько раз в SwiftUI
Краткое описание: в detailView у меня есть список связанной сущности. Для каждого элемента есть кнопка, чтобы открыть лист редактирования для этого элемента.
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))
При нажатии одной из кнопок редактирования в списке появляется лист с формой редактирования, предварительно заполненной значениями выбранного элемента списка.
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)
}
}
Но теперь при нажатии кнопки редактирования строки
- а) в отображаемой форме отображается только содержимое первого элемента списка
- б) нажав кнопку «Отмена» на листе, лист появится для каждого элемента в списке перед исчезновением, содержа соответствующие значения.
Небольшая отладка с print() показывает, что при нажатии кнопки редактирования сначала устанавливаются правильные значения (например, я нажимаю на третий элемент, значения третьего элемента передаются), но, кроме того, все элементы списка также передаются на лист .
1 ответ
Если у тебя есть
Есть несколько способов обойти это.
Опция 1 -
sheet(item:)
Замените свой
@State private var borrowerToEdit: Borrower? = nil
В действии кнопки установите это для заемщика текущей строки в цикле:
Button {
borrowerToEdit = borrower
} label: {
// etc
И, наконец, используйте
ForEach(...) { borrower in
// etc.
}
.sheet(item: $borrowerToEdit) { borrower in
EditBorrowerView(
aBorrower: borrower,
firstName: borrower.firstName!,
lastName: borrower.lastName!,
age: Int(borrower.age)
)
}
Вариант 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)
}
}
}
}
Таким образом, каждая строка имеет свое собственное логическое значение «должен ли я показывать модальное», поэтому не может быть путаницы в отношении того, какая строка «владеет» активным листом, и одновременно может отображаться только одна строка.
Какой вариант вы выберете, частично зависит от личных предпочтений. Я склоняюсь к варианту 1 в своем собственном коде, так как мне кажется, что модальное окно «принадлежит» списку, а не строке, и это подтверждает идею о том, что вы можете редактировать только одного заемщика за раз. Но любой подход должен устранить проблемы, с которыми вы сталкиваетесь в настоящее время.