Sheet 在 SwiftUI 中多次显示
Sheet displayed several times in SwiftUI
简短描述:在 detailView 中,我有一个相关实体的列表。对于每个项目,都有一个按钮可以打开该项目的编辑 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))
单击列表中的一个编辑按钮后,会出现一个带有编辑表单的 sheet,其中预先填充了所选列表项的值。
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)
}
}
但是现在,当点击一行的编辑按钮时
- a) 显示形式只显示第一个列表项的内容
- b) 单击 sheet 中的取消,sheet 将出现在列表中的每个项目之前消失,包含各自的值。
使用 print() 进行的小调试显示,在单击编辑按钮时,首先设置了正确的值(例如,我单击第三项,第三项的值被传递),但是另外,所有列表项都被传递也到 sheet。
First image as example of upper question
Second image as example of upper question
如果您在循环中重复的视图中有一个 .sheet(isPresented:)
修饰符,但您的布尔状态变量在循环之外,那么该状态变量将用于 [=43 的多个副本=].有时,这可能表现为 sheet 显示的详细信息与您单击的行不同;其他时候您可能会看到多个 sheet。听起来您看到的副作用与此有关。
有几种方法可以解决这个问题。
选项 1 - sheet(item:)
将您的 showingBorrowerEditScreen
替换为 borrowerToEdit
状态变量,它是一个可选对象,默认为 nil:
@State private var borrowerToEdit: Borrower? = nil
在您的按钮操作中,将其设置为循环中当前行的借款人:
Button {
borrowerToEdit = borrower
} label: {
// etc
最后,在 ForEach
循环 之外使用 sheet 修饰符 的 item:
形式。请注意,此表单采用一个块,其中包含对相关借款人对象的引用。
ForEach(...) { borrower in
// etc.
}
.sheet(item: $borrowerToEdit) { borrower in
EditBorrowerView(
aBorrower: borrower,
firstName: borrower.firstName!,
lastName: borrower.lastName!,
age: Int(borrower.age)
)
}
选项 2 - 单独的子视图
如果您想坚持使用布尔值来表示是否应该使用模态 sheet,则需要通过提取行详细信息将该布尔值隔离到它自己的子视图中。例如:
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)
}
}
}
}
这样一来,每一行都有自己的“我应该显示模式”布尔值,因此不会混淆哪一行“拥有”活动 sheet,并且只有一个能够一次显示。
您选择哪个选项部分取决于个人喜好。在我自己的代码中,我倾向于支持选项 1,因为它感觉模式更多地由列表而不是行“拥有”,并且它强化了一次只能编辑一个借款人的想法。但是这两种方法都应该可以消除您当前遇到的问题。
简短描述:在 detailView 中,我有一个相关实体的列表。对于每个项目,都有一个按钮可以打开该项目的编辑 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))
单击列表中的一个编辑按钮后,会出现一个带有编辑表单的 sheet,其中预先填充了所选列表项的值。
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)
}
}
但是现在,当点击一行的编辑按钮时
- a) 显示形式只显示第一个列表项的内容
- b) 单击 sheet 中的取消,sheet 将出现在列表中的每个项目之前消失,包含各自的值。
使用 print() 进行的小调试显示,在单击编辑按钮时,首先设置了正确的值(例如,我单击第三项,第三项的值被传递),但是另外,所有列表项都被传递也到 sheet。
First image as example of upper question
Second image as example of upper question
如果您在循环中重复的视图中有一个 .sheet(isPresented:)
修饰符,但您的布尔状态变量在循环之外,那么该状态变量将用于 [=43 的多个副本=].有时,这可能表现为 sheet 显示的详细信息与您单击的行不同;其他时候您可能会看到多个 sheet。听起来您看到的副作用与此有关。
有几种方法可以解决这个问题。
选项 1 - sheet(item:)
将您的 showingBorrowerEditScreen
替换为 borrowerToEdit
状态变量,它是一个可选对象,默认为 nil:
@State private var borrowerToEdit: Borrower? = nil
在您的按钮操作中,将其设置为循环中当前行的借款人:
Button {
borrowerToEdit = borrower
} label: {
// etc
最后,在 ForEach
循环 之外使用 sheet 修饰符 的 item:
形式。请注意,此表单采用一个块,其中包含对相关借款人对象的引用。
ForEach(...) { borrower in
// etc.
}
.sheet(item: $borrowerToEdit) { borrower in
EditBorrowerView(
aBorrower: borrower,
firstName: borrower.firstName!,
lastName: borrower.lastName!,
age: Int(borrower.age)
)
}
选项 2 - 单独的子视图
如果您想坚持使用布尔值来表示是否应该使用模态 sheet,则需要通过提取行详细信息将该布尔值隔离到它自己的子视图中。例如:
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)
}
}
}
}
这样一来,每一行都有自己的“我应该显示模式”布尔值,因此不会混淆哪一行“拥有”活动 sheet,并且只有一个能够一次显示。
您选择哪个选项部分取决于个人喜好。在我自己的代码中,我倾向于支持选项 1,因为它感觉模式更多地由列表而不是行“拥有”,并且它强化了一次只能编辑一个借款人的想法。但是这两种方法都应该可以消除您当前遇到的问题。