即使列表已正确更新,为什么上下文菜单仍显示旧状态?

Xav*_*oll 6 swiftui swiftui-list swiftui-state

我面临的问题是,Context Menu尽管List下面显示的是正确的数据,但显示的数据却是错误的。问题是,一旦触发第一个项目的上下文菜单上的操作,您将看到如何重新List渲染并显示正确的数据,但如果您再次触发第一个项目的上下文菜单,它将不会显示正确的状态。如果您打开第二个项目的上下文菜单,它将显示正确的状态,但如果您现在选择“Two”,并打开相同的上下文菜单,则会State出现错误(当它应该显示时,它将仅显示 1 个选定的项目) 1 和 2,就像List显示的那样)。

感觉好像偏离了一个(就像呈现以前的状态而不是最新的状态),我不确定这是否只是一个错误或者我使用错误。

这是重现该问题的代码片段:

@main
struct ContextMenuBugApp: App {
    
    let availableItems = ["One", "Two", "Three", "Four", "Five"]
    @State var selectedItems: [String] = []
    
    var body: some Scene {
        WindowGroup {
            List {
                ForEach(availableItems, id: \.self) { item in
                    HStack {
                        let isAlreadySelected = selectedItems.contains(item)
                        Text("Row \(item), selected: \(isAlreadySelected ? "true" : "false")")
                    }.contextMenu {
                        ForEach(availableItems, id: \.self) { item in
                            let isAlreadySelected = selectedItems.contains(item)
                            Button {
                                isAlreadySelected ? selectedItems.removeAll(where: { $0 == item }) : selectedItems.append(item)
                            } label: {
                                Label(item, systemImage: isAlreadySelected ? "checkmark.circle.fill" : "")
                            }
                        }
                    }
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

演示该问题的视频:https://twitter.com/xmollv/status/1412397838319898637

谢谢!

编辑:

这似乎是 iOS 15 的回归(至少在候选版本上),它在 iOS 14.6 上运行良好。

clu*_*der 7

您可以强制 contextMenu 使用任意 id 的背景视图重绘。IE:

\n
@main\nstruct ContextMenuBugApp: App {\n    let availableItems = ["One", "Two", "Three", "Four", "Five"]\n    @State var selectedItems: [String] = []\n    \n    func isAlreadySelected(_ item: String) -> Bool {\n        selectedItems.contains(item)\n    }\n    \n    var body: some Scene {\n        WindowGroup {\n            List {\n                ForEach(availableItems, id: \\.self) { item in\n                    HStack {\n                        Text("Row \\(item), selected: \\(isAlreadySelected(item) ? "true" : "false")")\n                    }\n                    .background(\n                        Color.clear\n                            .contextMenu {\n                                ForEach(availableItems, id: \\.self) { item in\n                                    Button {\n                                        isAlreadySelected(item) ? selectedItems.removeAll(where: { $0 == item }) : selectedItems.append(item)\n                                    } label: {\n                                        Label(item, systemImage: isAlreadySelected(item) ? "checkmark.circle.fill" : "")\n                                    }\n                                }\n                            }.id(selectedItems.count)\n                    )\n                }\n            }\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

如果它不起作用,您可以尝试将 id 放入 contextMenu 而没有背景(这可能基于 iOS 版本,它以前不起作用,所以要小心并测试以前的 iOS)

\n