Swiftui 从枚举创建列表

Swiftui 从枚举创建列表,swiftui,Swiftui,我希望能够从符合CaseIterable和CustomStringConverable的枚举创建一个列表,例如 公共枚举头发颜色:Int、可编码、可大小写、可自定义{ 公共变量说明:字符串{ 切换自身{ 黑色: 返回黑色 案例:金发碧眼: 金发碧眼 布朗先生: 返回棕色 红色: 返回红色 案例.灰色: 返回灰色 案例.秃头: 光头 } } 案例金发、棕色、黑色、红色、灰色、秃顶 } 结构ContentView:View{ var body:一些观点{ SwiftUIHelpers.enumToL

我希望能够从符合CaseIterable和CustomStringConverable的枚举创建一个列表,例如

公共枚举头发颜色:Int、可编码、可大小写、可自定义{ 公共变量说明:字符串{ 切换自身{ 黑色: 返回黑色 案例:金发碧眼: 金发碧眼 布朗先生: 返回棕色 红色: 返回红色 案例.灰色: 返回灰色 案例.秃头: 光头 } } 案例金发、棕色、黑色、红色、灰色、秃顶 } 结构ContentView:View{ var body:一些观点{ SwiftUIHelpers.enumToListHairColor } } 这是我尝试过的方法,但我得到了错误:无法将“Text”类型的值转换为“closure result type”_

结构快捷助手{ 静态函数枚举列表\ua:T->某些视图{ 名单{ ForEacha,id:\.rawValue{o:CustomStringConvertible in 文字说明 } } } }
我的方法有什么错误

这是有效的解决方案。使用Xcode 11.4/iOS 13.4进行测试

struct SwiftUIHelpers {
    static func enumToList<T: CaseIterable>(_ t: T.Type) -> some View 
           where T.AllCases: RandomAccessCollection, T: Hashable & CustomStringConvertible {
        List {
            ForEach(t.self.allCases, id: \.self) { o in
                Text(o.description)
            }
        }
    }
}

struct ContentView: View {
    var body: some View {
        SwiftUIHelpers.enumToList(HairColor.self)
    }
}

public enum HairColor: Int, Codable, Hashable, CaseIterable, CustomStringConvertible {
    public var description: String {
        switch self {
        case .black:
            return "Black"
        case .blond:
            return "Blond"
        case .brown:
            return "Brown"
        case .red:
            return "Red"
        case .grey:
            return "Gray"
        case .bald:
            return "Bald"
        }
    }
    case blond, brown, black, red, grey, bald
}
因为它提供了更广泛的重用可能性,比如

List { HairColor.toForEach() }
还有这个

Form { HairColor.toForEach() }


当然,在任何堆栈中,VStack{HairColor.toForEach}

并不是对你问题的所有部分的真正回答——只是第一部分——而是作为一种替代方案提供的

是否值得考虑将@EnvironmentObject用于@Published属性?我用它来填充macOS目标的边栏式菜单

步骤1:

使用您的枚举。我的枚举与你的枚举写得有点不同,但我想不写了,因为它提供了一种替代结构。。。但结果是一样的

符合CaseIterable允许我们在步骤2中使用.allCases方法

enum HairColor: Int, CaseIterable {

    case blond = 0, brown, black, red, grey, none

    var description: String {

        switch self {

        case .blond: return "Blond"
        case .brown: return "Brown"
        case .black: return "Black"
        case .red:   return "Red"
        case .grey:  return "Grey"
        case .none:  return "Bald"
        }
    }
}
步骤2:

为模型创建一个结构,并包含一个静态属性,该属性映射HairColor枚举的所有实例

符合此处的可识别性允许我们在步骤4中使用更干净的ForEach语法,即使用ForEachappData.hairolors代替ForEachappData.hairolors,id:\.id

步骤3:

创建一个符合ObserveObject的类,该类包含@Published wrapped属性,允许您通过@EnvironmentObject广播头发颜色

import Combine // <- don't forget this framework import!
import SwiftUI

final class AppData: ObservableObject {

    @Published var hairColors = Hair.colors
}
步骤5:

请记住将环境对象注入预览以使预览可用

struct HairList_Previews: PreviewProvider {

    static var previews: some View {

        HairList(selectedHair: .constant(AppData().hairColors[1]))
            .environmentObject(AppData())
    }
}

这肯定是越来越近了,谢谢!列表无法用ForEacht.self.allCases标识元素,id:\.self,您知道如何让ForEacht.self.allCases,id:\.rawValue编译吗?@Norman,如前所述,answer中提供的代码在运行时编译和测试。首先,谢谢你在这方面的帮助。虽然您提供的解决方案可以正确编译甚至显示,但由于使用了id:\.self,它没有为列表提供区分每个元素的机制。当您实际使用您建议的代码时,例如在表单选择器中,由于此问题,所选项目未注册。要有一个功能列表,其中的每个元素都是可识别的,因此需要使用id:\.rawValue。我已经在上发布了代码,以演示建议解决方案的问题。请注意,在使用更新的扩展名时,选择器不会保留所选的值。感谢您提供了如此周到且文档完整的解决方案!虽然它不能解决我目前的困境,但它让我思考了很多问题,例如,将枚举案例包装在一个可识别的包装器中,并向我介绍了SidebarListStyle,如果做一些Mac工作,我会将其归档。再次感谢您,您的回答非常清楚。
import SwiftUI

struct Hair: Codable, Hashable, Identifiable {

    var id: Int
    var name: String

    init(id: Int, name: String) {
        self.id = id
        self.name = name
    }

    static var colors: [Hair] {
        return HairColor.allCases.map({ Hair(id: $0.rawValue, name: $0.description ) })
    }
}
import Combine // <- don't forget this framework import!
import SwiftUI

final class AppData: ObservableObject {

    @Published var hairColors = Hair.colors
}
struct HairList: View {

    @EnvironmentObject var appData: AppData

    @State var selectedHair: Hair?

    var body: some View {

        VStack(alignment: .leading) {

            Text("Select...")
                .font(.headline)

            List(selection: $selectedHair) {

                ForEach(appData.hairColors) { hairColor in

                    Text(hairColor.name).tag(hairColor)
                }
            }
            .listStyle(SidebarListStyle())
        }
        .frame(minWidth: 100, maxWidth: 150)
        .padding()
    }
}
struct HairList_Previews: PreviewProvider {

    static var previews: some View {

        HairList(selectedHair: .constant(AppData().hairColors[1]))
            .environmentObject(AppData())
    }
}