You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 01:45:45