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

带关联属性的Swift枚举在SwiftUI View中使用的编译错误修复咨询

问题

拥有带关联属性的枚举CategoryType,想要在SwiftUI视图中列出所有case,但因关联属性无法使用CaseIterable和Identifiable协议,尝试通过计算属性allCases实现时出现编译错误:

Generic struct 'Picker' requires that 'CategoryType' conform to 'Hashable'
Referencing initializer 'init(_:content:)' on 'ForEach' requires that 'CategoryType' conform to 'Identifiable'

枚举代码:

enum CategoryType: Decodable, Equatable {
    case psDaily, psWeekly, psMonthly
    case unknown(value: String)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let status = try? container.decode(String.self)
        switch status {
        case "Daily": self = .psDaily
        case "Weekly": self = .psWeekly
        case "Monthly": self = .psMonthly
        default: self = .unknown(value: status ?? "unknown")
        }
    }
    
    var allCases: [CategoryType] {
        return [.psDaily, .psWeekly, .psMonthly]
    }
    
    var rawValue: String {
        switch self {
        case .psDaily: return "Daily"
        case .psWeekly: return "Weekly"
        case .psMonthly: return "Monthly"
        case .unknown: return "Unknown"
        }
    }
}

视图代码:

import SwiftUI

struct CategoryPicker: View {
    @Binding var selection: CategoryType

    var body: some View {
        NavigationStack {
            Form {
                Section {
                    Picker("Category", selection: $selection) {
                        ForEach(CategoryType().allCases) { category in
                            CategoryView(category: category)
                                .tag(category)
                        }
                    }
                }
            }
        }
    }
}

struct CategoryPicker_Previews: PreviewProvider {
    static var previews: some View {
        CategoryPicker(selection: .constant(.psDaily))
    }
}
解决方案

针对两个编译错误,分步骤修复:

1. 让CategoryType遵循Hashable协议

SwiftUI的Picker要求绑定的selection类型必须遵循Hashable,用于跟踪选中状态。由于Hashable继承自Equatable,且枚举的关联值String本身遵循Hashable,只需在枚举声明中添加Hashable即可自动合成实现:

enum CategoryType: Decodable, Equatable, Hashable { // 添加Hashable
    // 原有代码不变
}

2. 修复ForEach的标识问题

ForEach需要每个元素有唯一标识,有两种可选方案:

方案A:显式指定id参数

无需让枚举遵循Identifiable,直接在ForEach中通过id: \.self(因已遵循Hashable)或id: \.rawValue指定唯一标识,同时将allCases改为静态属性(无需创建枚举实例即可访问):

修改枚举的allCases:

static var allCases: [CategoryType] { // 改为static
    return [.psDaily, .psWeekly, .psMonthly]
}

修改视图中的ForEach:

ForEach(CategoryType.allCases, id: \.self) { category in
    CategoryView(category: category)
        .tag(category)
}

方案B:让CategoryType遵循Identifiable

通过扩展为枚举添加id属性,利用已有的rawValue作为唯一标识:

extension CategoryType: Identifiable {
    var id: String { rawValue }
}

此时ForEach可简化为:

ForEach(CategoryType.allCases) { category in
    CategoryView(category: category)
        .tag(category)
}

额外优化

建议修改rawValue中.unknown的返回值,返回实际关联的value而非固定字符串,更符合逻辑:

var rawValue: String {
    switch self {
    case .psDaily: return "Daily"
    case .psWeekly: return "Weekly"
    case .psMonthly: return "Monthly"
    case .unknown(let value): return value // 返回实际关联值
    }
}
完整修复后代码

枚举代码

enum CategoryType: Decodable, Equatable, Hashable {
    case psDaily, psWeekly, psMonthly
    case unknown(value: String)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let status = try? container.decode(String.self)
        switch status {
        case "Daily": self = .psDaily
        case "Weekly": self = .psWeekly
        case "Monthly": self = .psMonthly
        default: self = .unknown(value: status ?? "unknown")
        }
    }
    
    static var allCases: [CategoryType] {
        return [.psDaily, .psWeekly, .psMonthly]
    }
    
    var rawValue: String {
        switch self {
        case .psDaily: return "Daily"
        case .psWeekly: return "Weekly"
        case .psMonthly: return "Monthly"
        case .unknown(let value): return value
        }
    }
}

// 可选:遵循Identifiable
extension CategoryType: Identifiable {
    var id: String { rawValue }
}

视图代码

import SwiftUI

struct CategoryPicker: View {
    @Binding var selection: CategoryType

    var body: some View {
        NavigationStack {
            Form {
                Section {
                    Picker("Category", selection: $selection) {
                        // 若遵循了Identifiable,可省略id参数
                        ForEach(CategoryType.allCases) { category in
                            CategoryView(category: category)
                                .tag(category)
                        }
                    }
                }
            }
        }
    }
}

struct CategoryPicker_Previews: PreviewProvider {
    static var previews: some View {
        CategoryPicker(selection: .constant(.psDaily))
    }
}

内容的提问来源于stack exchange,提问作者LDropl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 05:20:06