如何从Array<any IRow>创建SwiftUI List?解决编译报错
问题描述
尝试用遵循IRow协议的类型数组创建SwiftUI List,在.onAppear中能正常映射并向下转型这些类型,但直接将数组作为List数据源时遇到编译错误:
- 直接使用
List(item.rows) { row in ... }时,编译器提示:'Type 'any IRow' cannot conform to ‘Identifiable’’
- 改用
List(item.rows, id: .self)时,编译器提示:‘cannot conform to ‘Hashable’’
相关代码
protocol IRow: Identifiable, Hashable { var id: UUID { get } var rowType: String { get } } struct TextItemRow: IRow { let id = UUID() let rowType = "text" var text: String = "" } struct HeadlineItemRow: IRow { let id = UUID() let rowType = "headline" var text: String = "" } struct Item: Identifiable { let id = UUID() var rows: [any IRow] = [HeadlineItemRow(text: "H_mock"), TextItemRow(text: "T_mock")] } struct ContentView: View { let item = Item() var body: some View { VStack { List(item.rows) { row in } } .onAppear { var _ = item.rows.map() { if let r = $0 as? HeadlineItemRow { print("\(r.id.uuidString) : \(r.text)") } else if let r = $0 as? TextItemRow { print("\(r.id.uuidString) : \(r.text)") } } } } }
解决方案
方案1:类型擦除包装器
创建一个类型擦除结构体,将any IRow包装为可识别、可哈希的类型:
struct AnyRow: Identifiable, Hashable { let id: UUID let rowType: String let base: any IRow init(_ row: any IRow) { self.id = row.id self.rowType = row.rowType self.base = row } static func == (lhs: AnyRow, rhs: AnyRow) -> Bool { lhs.id == rhs.id } func hash(into hasher: inout Hasher) { hasher.combine(id) } }
修改Item的rows属性为[AnyRow]:
struct Item: Identifiable { let id = UUID() var rows: [AnyRow] = [AnyRow(HeadlineItemRow(text: "H_mock")), AnyRow(TextItemRow(text: "T_mock"))] }
在ContentView中使用:
List(item.rows) { anyRow in switch anyRow.rowType { case "headline": if let headlineRow = anyRow.base as? HeadlineItemRow { Text(headlineRow.text) .font(.headline) } case "text": if let textRow = anyRow.base as? TextItemRow { Text(textRow.text) } default: EmptyView() } }
方案2:用枚举替代协议
如果行类型是固定的几种,枚举是更简洁的方案,天然支持Identifiable和Hashable:
enum Row: Identifiable, Hashable { case headline(text: String) case text(text: String) var id: UUID { UUID() // 可根据业务需求替换为稳定的ID生成逻辑 } var rowType: String { switch self { case .headline: return "headline" case .text: return "text" } } }
修改Item的rows属性:
struct Item: Identifiable { let id = UUID() var rows: [Row] = [.headline(text: "H_mock"), .text(text: "T_mock")] }
在ContentView中使用:
List(item.rows) { row in switch row { case .headline(let text): Text(text) .font(.headline) case .text(let text): Text(text) } }
问题原因
Swift的存在类型any IRow虽然协议继承了Identifiable和Hashable,但存在类型本身无法直接满足这些协议的要求。Hashable的hash(into:)和==方法依赖具体的Self类型,而存在类型是动态的,编译器无法确认其具体实现,因此会出现编译错误。
内容的提问来源于stack exchange,提问作者MatFetsch
相关产品推荐
相关产品推荐

