SwiftUI中如何在List中同时传入多个CaseIterable枚举数据源?
SwiftUI中同时展示三个CaseIterable枚举的对应内容
问题场景
你有三个实现了CaseIterable协议的枚举,希望在SwiftUI的List中同时展示三个枚举的对应项(名称、编码、数值),但目前只能单独传入一个枚举的数据源。由于枚举案例多达20个,不想手动逐个创建结构体实例(避免重复劳动),同时要保留枚举带来的类型安全、减少拼写错误的优势。
当前单独使用一个枚举的代码
VStack { List(OtherWoundsName.allCases, id: \.self) { name in Text(name.rawValue) } }
原手动创建结构体的方式(不希望使用)
struct BillingData { let name: String let code: String let value: String } var allOtherWounds = [ BillingData(name: OtherWoundsName.secondaryclosure_name.rawValue, code: OtherWoundsCode.secondaryclosure_code.rawValue, value: OtherWoundsValue.secondaryclosure_value.rawValue), BillingData(name: OtherWoundsName.musclerepair_name.rawValue, code: OtherWoundsCode.musclerepair_code.rawValue, value: OtherWoundsValue.musclerepair_value.rawValue), BillingData(name: OtherWoundsName.woundone_name.rawValue, code: OtherWoundsCode.woundone_code.rawValue, value: OtherWoundsValue.woundone_value.rawValue) ]
期望的理想写法
VStack { List(OtherWoundsName.allCases, OtherWoundsCode.allCases, OtherWoundsValue.allCases, id: \.self) { name, code, value in Text(name.rawValue) Text(code.rawValue) Text(value.rawValue) } }
可行实现方案
核心思路是利用Swift的序列合并能力,在保证三个枚举案例数量一致、顺序严格对应的前提下,将三个枚举的allCases序列合并,直接在List中使用合并后的序列,既保留类型安全,又无需手动创建结构体实例。
前提条件
确保三个枚举的案例顺序完全匹配,每个位置的案例对应同一业务项:
// 名称枚举 enum OtherWoundsName: String, CaseIterable { case secondaryclosure_name = "二期缝合" case musclerepair_name = "肌肉修复" case woundone_name = "伤口1" // 其余20个案例... } // 编码枚举(顺序与名称枚举完全对应) enum OtherWoundsCode: String, CaseIterable { case secondaryclosure_code = "CODE001" case musclerepair_code = "CODE002" case woundone_code = "CODE003" // 其余20个案例... } // 数值枚举(顺序与前两个枚举完全对应) enum OtherWoundsValue: String, CaseIterable { case secondaryclosure_value = "100" case musclerepair_value = "200" case woundone_value = "300" // 其余20个案例... }
方案1:使用嵌套zip合并序列(无需额外代码)
Swift标准库的zip函数可以合并两个序列,嵌套使用即可合并三个序列:
VStack { List(zip(zip(OtherWoundsName.allCases, OtherWoundsCode.allCases), OtherWoundsValue.allCases), id: \.0.0) { ((name, code), value) in VStack(alignment: .leading) { Text(name.rawValue) Text(code.rawValue).foregroundColor(.gray) Text(value.rawValue).foregroundColor(.blue) } } }
zip(zip(a,b),c)会将三个序列按索引配对,生成包含三个枚举案例的嵌套元组id: \.0.0使用第一个枚举案例作为唯一标识,保证List的稳定性
方案2:自定义zip3函数(更接近理想写法)
如果觉得嵌套zip可读性差,可以自定义一个支持合并三个序列的zip3函数:
// 自定义合并三个序列的工具函数 func zip3<A, B, C>(_ a: A, _ b: B, _ c: C) -> Zip3Sequence<A, B, C> where A: Sequence, B: Sequence, C: Sequence { Zip3Sequence(a, b, c) } struct Zip3Sequence<A: Sequence, B: Sequence, C: Sequence>: Sequence { typealias Element = (A.Element, B.Element, C.Element) private let a: A private let b: B private let c: C init(_ a: A, _ b: B, _ c: C) { self.a = a self.b = b self.c = c } func makeIterator() -> Iterator { Iterator(a.makeIterator(), b.makeIterator(), c.makeIterator()) } struct Iterator: IteratorProtocol { private var aIterator: A.Iterator private var bIterator: B.Iterator private var cIterator: C.Iterator init(_ a: A.Iterator, _ b: B.Iterator, _ c: C.Iterator) { self.aIterator = a self.bIterator = b self.cIterator = c } mutating func next() -> Element? { guard let a = aIterator.next(), let b = bIterator.next(), let c = cIterator.next() else { return nil } return (a, b, c) } } }
之后就可以用接近你理想的写法实现List:
VStack { List(zip3(OtherWoundsName.allCases, OtherWoundsCode.allCases, OtherWoundsValue.allCases), id: \.0) { name, code, value in VStack(alignment: .leading) { Text(name.rawValue) Text(code.rawValue).foregroundColor(.gray) Text(value.rawValue).foregroundColor(.blue) } } }
注意事项
- 必须保证三个枚举的案例数量完全一致,否则
zip会以最短的序列为准,多余的案例会被忽略 - 案例顺序必须严格对应,否则会出现数据匹配错误
- 两种方案都保留了枚举的类型安全特性,避免手动输入rawValue带来的拼写错误
内容的提问来源于stack exchange,提问作者Amar Cheema
相关产品推荐
相关产品推荐

