由枚举驱动的SwiftUI选择器:值未更新

10
根据苹果关于使用枚举来创建 SwiftUI 中的 Picker的文档,如果该枚举除了CaseIterable协议外还符合Identifiable协议,那么遍历所有情况的选择器应该本地更新绑定变量。 我进行了测试,但结果并不如预期。
enum Flavor: String, CaseIterable, Identifiable {
    case chocolate
    case vanilla
    case strawberry

    var id: String { self.rawValue }
}

struct EnumView: View {
    @State private var selectedFlavor = Flavor.chocolate
    var body: some View {
        VStack {
            Picker("Flavor", selection: $selectedFlavor) {
                ForEach(Flavor.allCases) { flavor in
                    Text(flavor.rawValue.capitalized)//.tag(flavor)
                }
            }
        
            Text("Selected flavor: \(selectedFlavor.rawValue)")
        }
    }
}

enter image description here

不过,如果我为每个视图传递一个tag标记,它就能正常工作。

enter image description here

这里发生了什么?苹果文档有误吗?selectedFlavor变量期望一个Flavor类型的值,但是在选择器中使用的id实际上是一个String。谢谢。
2个回答

16
为了使Picker正常工作,需要确定其元素。 请注意,selectedFlavor变量的类型为Flavor。这意味着 Picker 中的选项应该被标识为 Flavors(而不是 Strings)。 然而,在您的代码中,您的id的类型是String
var id: String { self.rawValue }

你可以选择:

  • 提供一个类型为Flavortag
Text(flavor.rawValue.capitalized)
    .tag(flavor)
  • 通过提供一个类型为Flavor的自定义id,将FlavorIdentifiable相一致:
var id: Flavor { self }
  • ForEach中明确指定id参数(类型为Flavor):
ForEach(Flavor.allCases, id: \.self) { ... }
  • selectedFlavor更改为字符串类型:
@State private var selectedFlavor = Flavor.chocolate.rawValue

但是当元素符合Identifiable时,ForEach应该自动为选择器中的选项视图分配标签,使用每个选项的id。我不明白为什么选择器不会自动将用作标记的字符串id转换为具有此id的rawValue作为标记的枚举。 - alpennec
@alpennec,我添加了更详细的解释。希望现在更清楚了。 - pawello2222
谢谢解释。列出所有可能的选项很好。如果我理解正确,当传递给ForEach循环的项目符合Identifiable(即我们不需要告诉id使用)时,ForEach循环中每个视图的生成标签就是id?因此,如果我们对枚举类型有一个字符串类型的id,则视图的标签为字符串,当选择器选择一个项目时,它选择一个字符串,而预期的选择值是枚举类型。这正确吗? - alpennec
@alpennec 是的,没错。基本上问题是关于给值打上字符串标签并期望呈现一种口味作为所选项。 - pawello2222

4
这是对 @pawello2222 已经很棒的答案的补充。
只需澄清一下,使用下面的代码将枚举符合 Identifiable 的解决方案可以正确地标记 UI 选择器,而无需在选择器代码中显式声明:
var id: Flavor { self }

官方SwiftUI picker文档指出,应使用var id: String { self.rawValue }标识枚举,并且在其余示例代码中不按预期工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接