SwiftUI结构体存储View属性、遵循Hashable/Equatable报错咨询
报错原因
- 第一个报错:Swift 中的
View是带有关联类型的协议,仅能作为泛型约束使用,不能直接声明为属性的类型,因此let view: View的写法本身不符合语法规则。 - 第二个报错:所有
View类型本身都不遵守Hashable/Equatable协议,你声明User结构体遵守Hashable时,编译器自动合成的协议实现要求所有存储属性都遵守Hashable,自然会触发不满足协议要求的报错。
修复方案
根据业务场景二选一即可:
方案1:泛型约束实现(同组User绑定的View类型完全一致时使用)
给User结构体添加View类型的泛型参数,同时手动实现Hashable协议,跳过View属性的判断逻辑:
struct User<V: View>: Hashable { let name: String let age: Int let profilePicture, status, icon: String let view: V // 仅通过用户基础属性判断相等、计算哈希,不涉及view属性 static func == (lhs: User, rhs: User) -> Bool { lhs.name == rhs.name && lhs.age == rhs.age && lhs.profilePicture == rhs.profilePicture && lhs.status == rhs.status && lhs.icon == rhs.icon } func hash(into hasher: inout Hasher) { hasher.combine(name) hasher.combine(age) hasher.combine(profilePicture) hasher.combine(status) hasher.combine(icon) } }
这个方案性能更好,没有额外的包装开销,但缺点是如果同一个数组里的不同User需要绑定不同类型的View,会因为泛型类型不匹配无法存入同一数组,这种场景请用方案2。
方案2:类型擦除包装实现(不同User可绑定不同类型View时使用)
用SwiftUI自带的类型擦除类型AnyView作为view属性的类型,把所有不同类型的View都统一包装成AnyView,同样手动实现Hashable跳过view属性的判断:
struct User: Hashable { let name: String let age: Int let profilePicture, status, icon: String let view: AnyView // 仅通过用户基础属性判断相等、计算哈希,不涉及view属性 static func == (lhs: User, rhs: User) -> Bool { lhs.name == rhs.name && lhs.age == rhs.age && lhs.profilePicture == rhs.profilePicture && lhs.status == rhs.status && lhs.icon == rhs.icon } func hash(into hasher: inout Hasher) { hasher.combine(name) hasher.combine(age) hasher.combine(profilePicture) hasher.combine(status) hasher.combine(icon) } } struct MyUserView: View { let users = [ User( name: "Manuelle", age: 23, profilePicture: "person", status: "inactive", icon: "message.fill", // 绑定视图时用AnyView做一层包装 view: AnyView(UserProfileView()) ), User( name: "Michael", age: 39, profilePicture: "person", status: "active 12 minutes ago", icon: "square.on.square", // 不需要绑定视图时传默认空视图即可 view: AnyView(EmptyView()) ) ] var body: some View { NavigationView { List { ForEach(users, id: \.self) { user in HStack { Text(user.name) Image(systemName: user.profilePicture) // 后续使用时直接取user.view即可,例如: // NavigationLink(destination: user.view) { /* 列表行内容 */ } } } } } } }
注意:不要依赖编译器自动合成
Hashable/Equatable实现,因为View本身无法做相等判断和哈希计算,手动实现时只需要把能标记用户唯一性的属性(比如用户id、名称等)纳入判断逻辑即可,不需要处理view属性。
内容的提问来源于stack exchange,提问作者kai
相关产品推荐
相关产品推荐

