从选择器 SwiftUI 中取消选择项目

Deselecting item from a picker SwiftUI

我使用带有选择器的表单,一切正常(我可以从选择器中 select 一个元素),但我不能 deselect 它。是否存在从选择器中删除select 项目的方法? 谢谢!

Picker(selection: $model.countries, label: Text("country")) {
                        ForEach(model.countries, id: \.self) { country in
                            Text(country!.name)
                                .tag(country)
                        }
                    }

要取消选择,我们需要选择器值的可选存储,因此这里是一个可能方法的演示。

测试 Xcode 12.1 / iOS 14.1

struct ContentView: View {
    @State private var value: Int?
    var body: some View {
        NavigationView {
            Form {
                let selected = Binding(
                    get: { self.value },
                    set: { self.value = [=10=] == self.value ? nil : [=10=] }
                )
                Picker("Select", selection: selected) {
                    ForEach(0...9, id: \.self) {
                        Text("\([=10=])").tag(Optional([=10=]))
                    }
                }
            }
        }
    }
}

backup

首先,我们可以固定选择。它应该与标签的类型相匹配。给定的标签是 Country,因此要在可能不选择任何内容的情况下进行选择,我们应该使用 Country? 作为 selection 类型。

它应该是这样的:

struct ContentView: View {
    
    @ObservedObject private var model = Model()
    @State private var selection: Country?
    
    var body: some View {
        NavigationView {
            Form {
                Picker(selection: $selection, label: Text("country")) {
                    ForEach(model.countries, id: \.self) { country in
                        Text(country!.name)
                            .tag(country)
                    }
                }
                
                Button("Clear") {
                    selection = nil
                }
            }
        }
    }
}

然后您只需将 selection 设置为 nil,这是在按钮中完成的。您可以通过任意操作将 selection 设置为 nil

如果您的部署目标设置为 iOS 14 或更高 -- Apple 为 View 提供了一个内置的 onChange 扩展,您可以在其中使用标签取消选择您的行,它可以像这样使用(谢谢)

Picker(selection: $favoriteColor, label: Text("Color")) {
    // ..
}
.onChange(of: favoriteColor) { print("Color tag: \([=10=])") }

通过阅读 this blog by Jim Dovey,我几乎了解了所有关于 SwiftUI 绑定(带有核心数据)的知识。剩下的就是一些研究和犯错误的许多小时的结合。

因此,当我结合 Jim 的技术在 SwiftUI Binding 上创建 Extensions 和 Asperi 的 时,我们最终会得到类似这样的东西...

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

然后可以像这样在整个代码中使用...

Picker("country", selection: Binding($selection, deselectTo: nil)) { ... }

Picker("country", selection: Binding($selection, deselectTo: someOtherValue)) { ... }