macOS SwiftUI列表右键菜单首次触发未正确识别目标项问题
问题分析与解决方案
这个Bug的核心原因是:macOS下首次触发ContextMenu时,actionTarget = value与isDeleting = true的状态更新存在时序问题——Sheet在actionTarget尚未完成赋值时就已经加载,导致首次显示初始的空字符串。后续操作时状态更新的时序趋于稳定,因此显示正常。
最可靠的修复方式是改用sheet(item:)绑定,而非isPresented:,因为item绑定会确保只有当目标对象被正确赋值后,Sheet才会被呈现。
修正后的代码
import SwiftUI struct ContentView: View { // 将actionTarget改为可选类型,初始值为nil @State private var actionTarget: Value? // 移除isDeleting状态,改用actionTarget的nil状态控制Sheet显示 @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 { Button { // 直接赋值actionTarget,Sheet会自动触发 actionTarget = value } label: { Text("Delete \(value.name)") } } } // 使用sheet(item:)绑定,绑定到actionTarget .sheet(item: $actionTarget) { target in Text("Ok to delete \"\(target.name)?\"") .frame(width: 300) .padding() .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Cancel") { actionTarget = nil } } ToolbarItem(placement: .destructiveAction) { Button { // 执行删除逻辑 if let index = values.firstIndex(of: target) { // 注意:values是let常量,实际项目中应改为@State数组 // 示例中仅演示流程 print("Deleted \(target.name)") } actionTarget = nil } label: { Text("Delete") } } } } } }
关键改动说明
- 将
actionTarget改为可选类型Value?,初始值设为nil,通过其是否为nil来控制Sheet的显示/隐藏,替代原有的isDeleting状态变量。 - 使用
sheet(item:)替代sheet(isPresented:),该绑定方式会自动监听actionTarget的变化,只有当它被赋值为有效对象时才会呈现Sheet,从根源上避免了状态更新的时序问题。 - Sheet内部直接使用传入的
target参数(即actionTarget的当前值),无需依赖外部状态,确保显示内容准确。
另外,注意示例中values是let常量,实际项目中如果需要执行删除操作,应将其改为@State var values,以便修改数组内容。
内容的提问来源于stack exchange,提问作者padeso
相关产品推荐
相关产品推荐

