在macOS SwiftUI应用程序中,我有一个带有上下文菜单的项目的List
。当选择菜单时,应用程序需要对正确的列表项进行操作。(上下文菜单可以应用于任何项目,而不仅仅是选中的项目。)
我有一个解决方案,工作得相当好,但它有一个奇怪的错误。当你右键点击(或Command+click)一个项目时,应用程序会设置一个变量,表明点击了哪个项目,还会设置一个标志。该标志触发一个请求确认动作的表。问题是,在第一次选择菜单项时,工作表不使用应该使用的保存项。您可以看到,因为项目的名称不在"Ok to delete"提示符中。如果您关闭第一个工作表并选择另一个项,它将正确工作,并且从那时起它将适用于每个后续项目,甚至是您尝试的第一个项目。不管你先尝试哪个项目,或者你是否先选择这个项目,或者其他任何事情。
import SwiftUI
struct ContentView: View {
@State private var actionTarget = Value(name: "")
@State private var isDeleting = false
@State private var selection = Value(name: "")
struct Value: Identifiable, Hashable {
let id = UUID()
var name: String
}
let values = [Value(name: "One"), Value(name: "Two"), Value(name: "Three")]
var body: some View {
List(values, selection: $selection) { value in
Text (value.name)
.tag(value)
.contextMenu(ContextMenu {
Button {
actionTarget = value
isDeleting = true
} label: { Text("Delete (value.name)") }
})
}
.sheet(isPresented: $isDeleting) {
Text("Ok to delete "(actionTarget.name)?"")
.frame(width: 300)
.padding()
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") { isDeleting = false }
}
ToolbarItem(placement: .destructiveAction) {
Button {
//TODO: Delete
isDeleting = false
} label: { Text("Delete") }
}
}
}
}
}
这是SwiftUI的一个bug。
您可以通过使用不同版本的sheet
修饰符来解决它,该修饰符采用Binding<Item?>
。这样做还有一个好处,那就是可以得到更好的数据模型。在你发布的模型中,你有单独的isDeleting
和actionTarget
变量,它们可能不同步。相反,使用单个可选变量保存要删除的Value
,如果没有要确认的删除,则使用nil。
struct ContentView: View {
@State private var deleteRequest: Value? = nil
@State private var selection: Value? = nil
struct Value: Identifiable, Hashable {
let id = UUID()
var name: String
}
let values = [Value(name: "One"), Value(name: "Two"), Value(name: "Three")]
var body: some View {
List(values, selection: $selection) { value in
Text(value.name)
.tag(value)
.contextMenu(ContextMenu {
Button {
deleteRequest = value
} label: { Text("Delete (value.name)") }
})
}
.sheet(
item: $deleteRequest,
onDismiss: { deleteRequest = nil }
) { item in
Text("Ok to delete "(item.name)?"")
.frame(width: 300)
.padding()
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") {
deleteRequest = nil
}
}
ToolbarItem(placement: .destructiveAction) {
Button {
print("TODO: delete (item)")
deleteRequest = nil
} label: { Text("Delete") }
}
}
}
}
}
但是在表单内使用工具栏看起来不像普通的macOS确认表单。相反,您应该使用confirmationDialog
。
struct ContentView: View {
@State private var deleteRequest: Value? = nil
@State private var selection: Value? = nil
struct Value: Identifiable, Hashable {
let id = UUID()
var name: String
}
let values = [Value(name: "One"), Value(name: "Two"), Value(name: "Three")]
var body: some View {
List(values, selection: $selection) { value in
Text(value.name)
.tag(value)
.contextMenu(ContextMenu {
Button {
deleteRequest = value
} label: { Text("Delete (value.name)") }
})
}
.confirmationDialog(
"OK to delete (deleteRequest?.name ?? "(nil)")?",
isPresented: .constant(deleteRequest != nil),
presenting: deleteRequest,
actions: { item in
Button("Cancel", role: .cancel) { deleteRequest = nil }
Button("Delete", role: .destructive) {
print("TODO: delete (item)")
deleteRequest = nil
}
}
)
}
}