swift 在视图中快速使用具有关联属性的枚举

hkmswyz6  于 2023-01-01  发布在  Swift
关注(0)|答案(2)|浏览(165)

我有一个带有关联属性的EnumCategoryType,我想在我的视图中使用它来列出来自enum的所有案例。
我可以使用CaseIterableIdentifiable作为我的枚举以及相关属性,这就是为什么它有点棘手。
我尝试使用计算属性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))
    }
}

如何解决这些问题,或者是否有其他方法来实现它?

p5fdfcr1

p5fdfcr11#

首先,按照编译器的要求,使枚举符合HashableIdentifiable

extension CategoryType: Hashable, Identifiable {
    var id: String { self.rawValue }
}

allCases属性与self无关,因此将其更改为static

static let allCases: [CategoryType] = {
    return [.psDaily, .psWeekly, .psMonthly]
}()

然后更改ForEach循环以使用此属性

ForEach(CategoryType.allCases) { category in
8dtrkrch

8dtrkrch2#

enum s很棒,但有时你需要一个struct。当可能有未知数时是一个完美的例子,因为你可以动态创建对象。

struct CategoryType: Codable, Equatable, CaseIterable, RawRepresentable, Identifiable, Hashable {
    //Conform to Identifiable using the rawValue
    var id: String{
        rawValue
    }
    //Keep the "String" for decoding and encoding
    var rawValue: String
    
    static let psDaily: CategoryType = .init(rawValue: "Daily")
    static let psWeekly: CategoryType = .init(rawValue: "Weekly")
    static let psMonthly: CategoryType =  .init(rawValue: "Monthly")
    
    init(rawValue: String) {
        self.rawValue = rawValue
    }
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        //Dont ignore errors
        let status = try container.decode(String.self)
        switch status {
        case "Daily": self = .psDaily
        case "Weekly": self = .psWeekly
        case "Monthly": self = .psMonthly
            //Create a custom `CategoryType` when the case is unknown.<---
        default:
            self.rawValue = status
        }            
    }
    //CaseIterable conformance
    static var allCases: [CategoryType] = [.psDaily, .psWeekly, .psMonthly]    
}

然后,您可以在View中使用它,这与enum非常相似,但它具有支持未知的优点。

struct CategoryPicker: View {
    @Binding var selection: CategoryType
    @State var string: String = ""
    @State var cases: [CategoryType] = CategoryType.allCases
    var body: some View {
        NavigationStack {
            Form {
                Text(string)
                
                    .onAppear(){
                        encode()
                    }
                Section {
                    Picker("Category", selection: $selection) {
                        //The loop now works as expected and has individual objects for the "unknown"
                        ForEach(cases) { category in
                            Text(category.rawValue)
                                .tag(category)
                        }
                    }
                }.onAppear(){
                    decode()
                }
            }
        }
    }
    //Sample encoding to demonstrate
    func encode() {
        let encoder: JSONEncoder = .init()
        encoder.outputFormatting = .prettyPrinted
        do{
            let data = try encoder.encode(CategoryType.allCases)
            string = String(data: data, encoding: .utf8) ?? "failed"
        }catch{
            print(error)
        }
    }
    //Sample decoding that inclues "unknown" cases and creates object
    func decode() {
        let json = """
        ["Daily", "Weekly", "Monthly", "Unknown", "Something Unique"]
""".data(using: .utf8)!
        let decoder: JSONDecoder = .init()
        do{
            cases = (try decoder.decode([CategoryType].self, from: json))
        }catch{
            print(error)
        }
    }
}

相关问题