【问题标题】:onDeleteCommand menu item is always enabledonDeleteCommand 菜单项始终启用
【发布时间】:2021-08-18 22:48:50
【问题描述】:

我正在使用 macOS Monterey Beta 5 和 Xcode 13 Beta 5,我遇到了 onDeleteCommand 的问题。基本上,即使没有选择任何项目,它的菜单项也不会禁用。

我试过deleteDisabled,但没有用。

复制方法如下:

  1. 新建一个空白的 SwiftUI macOS 项目
  2. 粘贴以下代码:
struct ContentView: View {
    @State var data = ["Item1", "Item2", "Item3"]
    @State var selection: Set<String> = []
    
    var body: some View {
        NavigationView {
            List(data, id: \.self, selection: $selection) { item in
                Text(item)
            }
            .onDeleteCommand {
                data.removeAll(where: selection.contains) // Removes selected items
                selection.removeAll()
            }
            .deleteDisabled(selection.isEmpty) // This doesn't help either
            Text("Second Panel")
        }
    }
}
  1. 运行应用程序,然后选择一个项目。
  2. 进入菜单,然后按编辑 -> 删除。现在选定的项目应该已经消失了,但编辑 -> 删除菜单项仍然处于启用状态。

我能做些什么来解决这个问题(让菜单项自行禁用)?

任何帮助将不胜感激。

【问题讨论】:

    标签: swift macos swiftui


    【解决方案1】:

    一种可能的方法是通过更改视图的 ID 来强制更新 UI。我们应该只在必要时更改它,即在每次删除之后。

    我创建了一个@State 变量lastForcedId,每次运行onDeleteCommand 时都会给它一个不同的值。

    例子:

    struct ContentView: View {
        @State private var data = ["Item1", "Item2", "Item3"]
        @State private var selection: Set<String> = []
        @State private var lastForcedId = UUID()
    
        var body: some View {
            NavigationView {
                List(data, id: \.self, selection: $selection) { item in
                    Text(item)
                }
                .onDeleteCommand {
                    data.removeAll(where: selection.contains) // Removes selected items
                    selection.removeAll()
                    lastForcedId = UUID()
                }
                .id(lastForcedId)
    
                Text("Second Panel")
            }
        }
    }
    

    结果:

    【讨论】:

    • 感谢您的帮助!这很好用,但我想知道这是预期的 SwiftUI 行为吗? (就像这是一个 SwiftUI 错误?)
    • @recaptcha 可能是 SwiftUI 错误。我做了一个类似的回答,我认为最近有点类似的问题。不幸的是,这是当前解决这个问题的方法(但苹果实际上已经解决了这样的更新错误)
    • 啊好的。我就此向苹果提交了一份错误报告。不过感谢您的帮助!
    • @recaptcha 找到了我引用的答案 (here),尽管我的答案不是最好的。值得注意的是,出于某种奇怪的原因,在那个例子中,onChange 没有被调用——这意味着 SwiftUI 没有像在某些情况下通常应该在视图中那样跟踪@State。这是相关的,因为它与应用程序的菜单栏 UI 未更新有关。
    • 我认为苹果实际上在 Beta 5 中修复了该菜单栏问题(我不久前提交了关于该问题的错误报告)。
    【解决方案2】:

    文档没有说明这一点(我提交了一份报告),但您可以将nil 作为操作传递给大多数(全部?)on*Command(perform:) 修饰符以显式禁用关联的菜单项。这个稍微改写的ContentView 应该得到正确的行为:

    struct ContentView: View {
        @State var data = ["Item1", "Item2", "Item3"]
        @State var selection: Set<String> = []
    
        var body: some View {
            NavigationView {
                List(data, id: \.self, selection: $selection) { item in
                    Text(item)
                }
                .onDeleteCommand(perform: selection.isEmpty ? nil : deleteSelection)
                Text("Second Panel")
            }
        }
    
        private func deleteSelection() {
            data.removeAll { selection.contains($0) }
            selection.removeAll()
        }
    }
    

    虽然效果可能与接受的答案基本相同,但传递 nil 而不是操作块通常是一种更可靠的方法,因为更改视图身份有时会产生令人惊讶的后果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-14
      相关资源
      最近更新 更多