SwiftUI中可选数据类型的选择器?
通常,我可以在SwiftUI中显示如下项目列表:SwiftUI中可选数据类型的选择器?,swiftui,Swiftui,通常,我可以在SwiftUI中显示如下项目列表: enum Fruit { case apple case orange case banana } struct FruitView: View { @State private var fruit = Fruit.apple var body: some View { Picker(selection: $fruit, label: Text("Fruit")) {
enum Fruit {
case apple
case orange
case banana
}
struct FruitView: View {
@State private var fruit = Fruit.apple
var body: some View {
Picker(selection: $fruit, label: Text("Fruit")) {
ForEach(Fruit.allCases) { fruit in
Text(fruit.rawValue).tag(fruit)
}
}
}
}
这非常有效,允许我选择我想要的任何水果。但是,如果我想将fruit
切换为可为空(也称为可选),则会导致以下问题:
struct FruitView: View {
@State private var fruit: Fruit?
var body: some View {
Picker(selection: $fruit, label: Text("Fruit")) {
ForEach(Fruit.allCases) { fruit in
Text(fruit.rawValue).tag(fruit)
}
}
}
}
选定的水果名称不再显示在第一个屏幕上,并且无论我选择什么选择项,它都不会更新水果值
如何将Picker与可选类型一起使用?绑定包装时,标记必须与确切的数据类型匹配。在这种情况下,
tag
提供的数据类型是Fruit
,但$Fruit.wrappedValue
的数据类型是Fruit?
。您可以通过在标记
方法中强制转换数据类型来解决此问题:
struct FruitView: View {
@State private var fruit: Fruit?
var body: some View {
Picker(selection: $fruit, label: Text("Fruit")) {
ForEach(Fruit.allCases) { fruit in
Text(fruit.rawValue).tag(fruit as Fruit?)
}
}
}
}
奖金:如果您希望为nil
自定义文本(而不是仅为空),并且希望允许用户选择nil
(注意:这里要么全部,要么什么都没有),您可以为nil
添加一个项目:
struct FruitView: View {
@State private var fruit: Fruit?
var body: some View {
Picker(selection: $fruit, label: Text("Fruit")) {
Text("No fruit").tag(nil as Fruit?)
ForEach(Fruit.allCases) { fruit in
Text(fruit.rawValue).tag(fruit as Fruit?)
}
}
}
}
不要忘记强制转换
nil
值。为什么不使用默认值扩展枚举?如果这不是您想要实现的目标,也许您还可以提供一些信息,说明您为什么希望它是可选的
enum Fruit: String, CaseIterable, Hashable {
case apple = "apple"
case orange = "orange"
case banana = "banana"
case noValue = ""
}
struct ContentView: View {
@State private var fruit = Fruit.noValue
var body: some View {
VStack{
Picker(selection: $fruit, label: Text("Fruit")) {
ForEach(Fruit.allCases, id:\.self) { fruit in
Text(fruit.rawValue)
}
}
Text("Selected Fruit: \(fruit.rawValue)")
}
}
}
我在这里用Senssuve的解决方案进行了公开回购: 编辑:感谢您对发布链接的评论。下面是回答问题的代码。复制/粘贴就可以了,或者从链接克隆回购协议
import SwiftUI
struct ContentView: View {
@State private var selectionOne: String? = nil
@State private var selectionTwo: String? = nil
let items = ["Item A", "Item B", "Item C"]
var body: some View {
NavigationView {
Form {
// MARK: - Option 1: NIL by SELECTION
Picker(selection: $selectionOne, label: Text("Picker with option to select nil item [none]")) {
Text("[none]").tag(nil as String?)
.foregroundColor(.red)
ForEach(items, id: \.self) { item in
Text(item).tag(item as String?)
// Tags must be cast to same type as Picker selection
}
}
// MARK: - Option 2: NIL by BUTTON ACTION
Picker(selection: $selectionTwo, label: Text("Picker with Button that removes selection")) {
ForEach(items, id: \.self) { item in
Text(item).tag(item as String?)
// Tags must be cast to same type as Picker selection
}
}
if selectionTwo != nil { // "Remove item" button only appears if selection is not nil
Button("Remove item") {
self.selectionTwo = nil
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
实际上,我更喜欢@sensufe的解决方案作为点解决方案,但对于后代来说:您还可以创建一个包装器枚举,如果您的应用程序中有大量的实体类型,那么它可以通过协议扩展非常好地扩展
//用于确保可以生成默认id的实用程序约束
协议可初始化{
init()
}
//PickerValue包装器上的主约束
协议可选取{
associatedtype元素:可识别,其中Element.ID:EmptyInitializable
}
//隐藏可选性的包装器
枚举PickerValue:Pickable其中元素:可识别,元素ID:EmptyInitializable{
无案例
案例(要素)
}
//包装器上的hashable&eqtable
扩展PickerValue:Hashable和Equatable{
func散列(放入散列程序:inout散列程序){
hasher.combine(id)
}
静态函数==(左:自,右:自)->Bool{
lhs.id==rhs.id
}
}
//常见可识别类型
扩展字符串:EmptyInitializable{}
扩展名Int:EmptyInitializable{}
扩展UInt:EmptyInitializable{}
扩展UInt8:EmptyInitializable{}
扩展UInt16:EmptyInitializable{}
扩展UInt32:EmptyInitializable{}
扩展UInt64:EmptyInitializable{}
扩展UUID:EmptyInitializable{}
//包装器上的id生成器
扩展选择器值:可识别{
变量id:Element.id{
切换自身{
案例。一些(让e):
返回e.id
案例:无:
返回元素。ID()
}
}
}
//要包装为PickerValue的数组上的实用程序扩展
扩展数组,其中Element:可识别,Element.ID:EmptyInitializable{
变量可选取:数组{
地图{.some($0)}
}
var optionalPickable:数组{
[.none]+可拾取
}
}
//使用协议包装的好处是项目视图可以是通用的
//跨数据集。(此处TitleComponent{var title:String{get})
扩展选择器值,其中元素:TitleComponent{
@视图生成器
var itemView:一些视图{
团体{
切换自身{
案例。一些(让e):
文本(如标题)
案例:无:
文本(“无”)
.italic()
.foregroundColor(.accentColor)
}
}
.tag(self)
}
}
使用量相当紧张:
Picker(选择:$task.job,标签:Text(“job”)){
ForEach(Model.shared.jobs.optionalpikable){p in
p、 项目视图
}
}
你能分享你的水果吗/struct@SimonBachmann:这是一个枚举,更新了问题您的评论对于枚举
来说足够公平,但是如果它是从数组或集合中选择的,而“以上任何一项”都不是有效的选择,该怎么办@SENSSEVE的回答非常有用!请不要只是发布答案的链接。发布相关代码和链接。这样,您的答案不会消失,它本可以提供的价值也会丢失。虽然此链接可以回答问题,但最好在此处包含答案的基本部分,并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能无效。-我很感激,先生们(@Andrew,@kenny_k)。编辑!此语法的一些变体:.tag(nil作为水果?
,.tag(水果?.none)
,.tag(水果?(nil))
,.tag(可选.none)
。