Swift中如何通过switch匹配外层枚举获取对应嵌套Enum的allCases
问题原因
你的代码无法正常运行核心是三个问题:
- 定义的
UnitProtocol是空协议,没有约定实现类必须提供allCases、rawValue属性,编译器无法判定遵守协议的类型是否支持遍历、是否能取到展示用的文本值。 - 各嵌套单位枚举(Temperature/Length/Time/Volume)是完全独立的具体类型,你原来的计算属性返回
UnitProtocol类型会触发类型擦除,无法直接传递给ForEach做遍历;且你返回的是枚举元类型(Units.Temperature.Type),不是可遍历的实例集合。 - 给具体单位选择器绑定的
$units是外层单位大类的枚举类型,和内层具体单位的类型完全不匹配,无法做选择值绑定。
实现方案
不需要推翻你现有的嵌套枚举结构,只需要做几处调整即可:
- 补全
UnitProtocol的能力约定,要求所有遵守协议的单位类型必须支持遍历、拥有String类型的rawValue - 拆分状态:单独定义单位大类、源单位、目标单位三个状态,其中具体单位用String类型存储rawValue,解决不同类型单位无法统一存储的问题
- 用计算属性直接返回当前大类对应的具体单位字符串数组,统一遍历元素的类型,避免元类型、类型擦除带来的问题
- 增加大类切换的回调,切换大类时自动重置默认选中的具体单位,避免逻辑异常
第一步:调整协议与枚举定义
// 协议约定所有单位类型必须支持遍历、rawValue为String类型 protocol UnitProtocol: CaseIterable, RawRepresentable where RawValue == String {} enum Units: String, CaseIterable { case temperature, length, time, volume // 所有嵌套具体单位枚举遵守改造后的UnitProtocol enum Temperature: String, UnitProtocol { case celsius, fahrenheit, kelvin } enum Length: String, UnitProtocol { case meters, kilometers, feet, yards, miles } enum Time: String, UnitProtocol { case seconds, minutes, hours, days } enum Volume: String, UnitProtocol { case milliliters, liters, cups, pints, gallons } }
第二步:调整视图逻辑
struct ContentView: View { // 当前选中的单位大类 @State private var selectedCategory = Units.temperature // 选中的源转换单位、目标转换单位,用rawValue字符串统一存储 @State private var sourceUnit = "" @State private var targetUnit = "" // 返回当前大类对应的所有具体单位,统一为[String]类型,可直接给ForEach遍历 private var availableUnits: [String] { switch selectedCategory { case .temperature: return Units.Temperature.allCases.map(\.rawValue) case .length: return Units.Length.allCases.map(\.rawValue) case .time: return Units.Time.allCases.map(\.rawValue) case .volume: return Units.Volume.allCases.map(\.rawValue) } } var body: some View { NavigationView { Form { Section { Picker("Units to convert", selection: $selectedCategory) { ForEach(Units.allCases, id: \.self) { Text($0.rawValue.capitalized) } } // 切换大类时重置默认选中的具体单位 .onChange(of: selectedCategory) { _ in sourceUnit = availableUnits.first ?? "" targetUnit = availableUnits.first ?? "" } } Section { Picker("Convert from", selection: $sourceUnit) { ForEach(availableUnits, id: \.self) { Text($0.capitalized) } } } Section { Picker("Convert to", selection: $targetUnit) { ForEach(availableUnits, id: \.self) { Text($0.capitalized) } } } } .navigationTitle("Unit Converter") // 首次加载时初始化默认选中的单位 .onAppear { sourceUnit = availableUnits.first ?? "" targetUnit = availableUnits.first ?? "" } } } }
方案说明
- 完全保留你原本设计的嵌套枚举结构,后续新增单位类型只需要新增对应嵌套枚举、在
availableUnits的switch分支中加一行映射即可,维护成本很低 - 所有遍历用的数组类型统一为
[String],不存在类型不匹配、元类型无法遍历的编译问题 - 选择器的绑定值类型和选中值类型完全对齐,不会出现绑定错误
- 切换大类时自动重置选中值,不会出现选中值和当前展示列表不匹配的逻辑问题
内容的提问来源于stack exchange,提问作者hisikig
相关产品推荐
相关产品推荐

