【问题标题】:Deselect Items from SwiftUI List on macOS with single click在 macOS 上通过单击从 SwiftUI 列表中取消选择项目
【发布时间】:2022-01-18 08:35:29
【问题描述】:

我正在开发一个带有List 视图和可选行的 macOS 应用程序。 因为很遗憾没有editMode

在 macOS 上,单击选择单元格是可能的,但取消选择已选择的单元格执行相同的操作不会执行任何操作。 取消选择单元格的唯一选项是CMD + Click,这不是很直观。
最小示例:

struct RowsView: View {
    
    @State var selectKeeper: String?
    
    let rows = ["1", "2", "3", "4", "5", "6", "7", "8"]
    
    var body: some View {
        List(rows, id: \.self, selection: $selectKeeper) { row in
            Text(row)
        }
    }
}

struct RowsView_Previews: PreviewProvider {
    static var previews: some View {
        RowsView()
    }
}

单击或双击第 3 行不执行任何操作,并且该行保持选中状态。


直接附加绑定
我已尝试按照Picker here 的出色答案中所述直接附加Binding,但这似乎不适用于macOS 上的List

...
    var body: some View {
        List(rows, id: \.self, selection: Binding($selectKeeper, deselectTo: nil)) { row in
            Text(row)
        }
    }
...

public extension Binding where Value: Equatable {
                
    init(_ source: Binding<Value>, deselectTo value: Value) {
        self.init(get: { source.wrappedValue },
                  set: { source.wrappedValue = $0 == source.wrappedValue ? value : $0 }
        )
    }
}

关于如何在不重建选择机制的情况下实现单击取消选择的任何想法?

备案:XCode 13.2.1,macOS BigSur 11.6.2

【问题讨论】:

  • 确认一下,您要单选还是多选单元格/行?
  • 多选,但我引导示例以使其更容易。这两种方式都不起作用

标签: list macos swiftui selection


【解决方案1】:

我们可以通过使用自己的手势来阻止默认点击处理并手动管理选择。

这里是可能的方法的演示。使用 Xcode 13.2 / macOS 12.1 测试

struct RowsView: View {

    @State var selectKeeper: String?

    let rows = ["1", "2", "3", "4", "5", "6", "7", "8"]

    var body: some View {
        List(rows, id: \.self, selection: $selectKeeper) { row in
            Text(row)
                .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
                .contentShape(Rectangle())      // handle click row-wide
                .listRowInsets(EdgeInsets())    // remove default edges
                .onTapGesture {
                    selectKeeper = selectKeeper == row ? nil : row   // << here !!
                }
                .padding(.vertical, 4)          // look&feel like default
        }
    }
}

【讨论】:

  • 几乎完美,谢谢,我已经对单元格进行了着色,以便容易看到丢失的、不可点击的部分:i.stack.imgur.com/eFldR.jpg 不幸的是,这是最难安装的部分,因为它超出了框架细胞。
【解决方案2】:

我将this answer 称为标题为“Select Multiple Items in a SwiftUI List”的 SO 帖子。

根据这个答案,我生成了代码来选择列表中的多个项目。

在我的示例代码中,我使用复选标记来说明是否选择了一行,但您可以根据需要进行更改。

您需要将 @State 包装器更改为 Set,因为您确认需要选择一组项目。 (顺便说一句,Apple 建议您将 @State 属性包装器标记为 private 以便在本地加强它们的预期用途。)

@State private var selectedItems: Set<String>?
let rows = ["1", "2", "3", "4", "5", "6", "7", "8"]
var body: some View {
    List(rows, id: \.self) { item in            
        RowSelectable(selectedItems: $selectedItems, rowItem: item)
    }
}

RowSelectable 在哪里...

struct RowSelectable: View {
    @Binding var selectedItems: Set<String>?
    var rowItem: String
    var isSelected: Bool {
        return selectedItems?.contains(rowItem) == true
    }
    var body: some View {
        HStack {
            Text(rowItem)
            if selectedItems?.contains(rowItem) == true {
                Spacer()
                Image(systemName: "checkmark")
            }
        }
        .onTapGesture(count: 1) {
            if self.isSelected {
                selectedItems!.remove(rowItem)
            }
            else {
                selectedItems!.insert(rowItem)
            }
        }
    }
}

我还没有测试过,所以如果它不起作用,请告诉我,我会检查 Xcode。

【讨论】: