协议类型的Swift值';XXX和x27;不能符合';可识别的;;只有结构/枚举/类类型才能符合协议

协议类型的Swift值';XXX和x27;不能符合';可识别的;;只有结构/枚举/类类型才能符合协议,swift,generics,enums,swift-protocols,Swift,Generics,Enums,Swift Protocols,我得到以下错误: 协议类型“menuplotocol”的值不能与“可识别”一致;只有结构/枚举/类类型可以符合协议 我有一个主菜单,它的一些选项有一个子菜单。我决定对主菜单使用enum,对任何子菜单使用另一个enum。所有枚举都实现了我定义的一个协议,它允许我为每个菜单选项指定要显示的文本 我不确定这是否是最好的方法,但我发现它很有用 有什么办法可以纠正这个错误吗?谢谢你的帮助 Protocol MenuProtocol { var submenu: [OptionProtocol] {

我得到以下错误:

协议类型“menuplotocol”的值不能与“可识别”一致;只有结构/枚举/类类型可以符合协议

我有一个主菜单,它的一些选项有一个子菜单。我决定对主菜单使用
enum
,对任何子菜单使用另一个
enum
。所有枚举都实现了我定义的一个协议,它允许我为每个菜单选项指定要显示的文本

我不确定这是否是最好的方法,但我发现它很有用

有什么办法可以纠正这个错误吗?谢谢你的帮助

Protocol MenuProtocol {
    var submenu: [OptionProtocol] { get }
}

protocol OptionProtocol {
    var name: String { get }
}

enum MainMenu: Int, Identifiable, CaseIterable, MenuProtocol, OptionProtocol {
    case One
    case Two
    case Three
    case Four
    case Five
    
    var id: Int { rawValue }
    
    var name: String {
        switch self {
        case .One:
            return "One"
        case .Two:
            return "Two"
        case .Three:
            return "Three"
        case .Four:
            return "Four"
        case .Five:
            return "Five"
        }
    }
    
    var submenu: [OptionProtocol] {
        switch self {
        case .One:
            return SubMenu1.allCases
        case .Two:
            return SubMenu2.allCases
        default:
            return []
        }
    }
}

enum SubMenu1: Int, Identifiable, CaseIterable, OptionProtocol {
    case SubMenu1_Option1
    case SubMenu1_Option2
    case SubMenu1_Option3
    case SubMenu1_Option4
    case SubMenu1_Option5
    
    var id: Int { rawValue }
    
    var name: String {
        switch self {
        case .SubMenu1_Option1:
            return "Submenu1 Option 1"
        case .SubMenu1_Option2:
            return "Submenu1 Option 2"
        case .SubMenu1_Option3:
            return "Submenu1 Option 3"
        case .SubMenu1_Option4:
            return "Submenu1 Option 4"
        case .SubMenu1_Option5:
            return "Submenu1 Option 5"
        }
    }
}

enum SubMenu2: Int, Identifiable, CaseIterable, OptionProtocol {
    case SubMenu2_OptionA
    case SubMenu2_OptionB
    case SubMenu2_OptionC
    case SubMenu2_OptionD
    case SubMenu2_OptionE
    
    var id: Int { rawValue }
    
    var name: String {
        switch self {
        case .SubMenu2_OptionA:
            return "Submenu2 Option A"
        case .SubMenu2_OptionB:
            return "Submenu2 Option B"
        case .SubMenu2_OptionC:
            return "Submenu2 Option C"
        case .SubMenu2_OptionD:
            return "Submenu2 Option D"
        case .SubMenu2_OptionE:
            return "Submenu2 Option E"
        }
    }
}

struct EnumProtTest: View {
    var body: some View {
        VStack {
            HStack {
                ForEach(MainMenu.allCases) { value in
                    Text("\(theName(value))")
                        .padding()
                        .background(Color.blue)
                }
            }
            
            HStack {
                TheContentView(data: MainMenu.One.submenu) { item in
                    Text("\(item.name)")
                        .padding()
                        .background(Color.purple)
                }
            }
            
            HStack {
                TheContentView(data: MainMenu.Two.submenu) { item in
                    Text("\(item.name)")
                        .padding()
                        .background(Color.purple)
                }
            }
        }
    }
}

struct TheContentView<Data: RandomAccessCollection, ElementView: View>: View where Data.Element: Identifiable, Data.Element: Hashable {
    
    var data: Data
    var itemView: (Data.Element) -> ElementView
    
    var body: some View {
        ForEach(data) { item in
            itemView(item)
                .padding()
                .background(Color.purple)
        }
    }
}
协议MenuProtocol{
var子菜单:[OptionProtocol]{get}
}
协议选择协议{
变量名称:字符串{get}
}
枚举主菜单:Int、可识别、可大小写、菜单目录、选项协议{
案例一
案例二
案例三
案例四
案例五
变量id:Int{rawValue}
变量名称:String{
切换自身{
案例一:
返回“一”
案例二:
返回“2”
案例三:
返回“三”
案例四:
返回“四”
案例五:
返回“五”
}
}
var子菜单:[选项协议]{
切换自身{
案例一:
返回子菜单1.allCases
案例二:
返回子菜单2.allCases
违约:
返回[]
}
}
}
枚举子菜单1:Int,可识别,可大小写,OptionProtocol{
案例子菜单1_选项1
案例子菜单1\u选项2
案例子菜单1\u选项3
案例子菜单1\u选项4
案例子菜单1\u选项5
变量id:Int{rawValue}
变量名称:String{
切换自身{
案例。子菜单1_选项1:
返回“子菜单1选项1”
案例。子菜单1_选项2:
返回“子菜单1选项2”
案例。子菜单1\u选项3:
返回“子菜单1选项3”
案例。子菜单1\u选项4:
返回“子菜单1选项4”
案例。子菜单1\u选项5:
返回“子菜单1选项5”
}
}
}
枚举子菜单2:Int,可识别,可大小写,OptionProtocol{
案例子菜单2_OptionA
案例子菜单2_选项B
案例子菜单2\u选项C
案例子菜单2\u选项
案例子菜单2_OptionE
变量id:Int{rawValue}
变量名称:String{
切换自身{
案例。子菜单2_选项a:
返回“子菜单2选项A”
案例。子菜单2_选项B:
返回“子菜单2选项B”
案例。子菜单2\u选项C:
返回“子菜单2选项C”
案例。子菜单2_选项d:
返回“子菜单2选项D”
案例。子菜单2_选项一:
返回“子菜单2选项E”
}
}
}
结构枚举测试:视图{
var body:一些观点{
VStack{
HStack{
ForEach(main menu.allCases){中的值
文本(\(名称(值)))
.padding()
.背景(颜色.蓝色)
}
}
HStack{
ContentView(数据:主菜单.One.子菜单){item in
文本(“\(item.name)”)
.padding()
.背景(颜色.紫色)
}
}
HStack{
ContentView(数据:主菜单.Two.子菜单){item in
文本(“\(item.name)”)
.padding()
.背景(颜色.紫色)
}
}
}
}
}
struct TheContentView:View where Data.Element:identificatable,Data.Element:Hashable{
var数据:数据
var itemView:(Data.Element)->ElementView
var body:一些观点{
ForEach(数据){中的项目
项目视图(项目)
.padding()
.背景(颜色.紫色)
}
}
}

您可以使用
索引
并使用索引检索数据。只需更改您的
HStack

HStack {
    ForEach(MainMenu.One.items().indices) { index in
        let value = MainMenu.One.items()[index]
        Text(value.name())
            .padding()
            .background(Color.purple)
    }
}

您可以使用
索引
并使用索引检索数据。只需更改您的
HStack

HStack {
    ForEach(MainMenu.One.items().indices) { index in
        let value = MainMenu.One.items()[index]
        Text(value.name())
            .padding()
            .background(Color.purple)
    }
}

ForEach
需要一个
可识别的
类型,这就是为什么您的菜单*-类型符合
可识别的
。但是传递给
ForEach
的是一个不符合
可识别的
MenuProtocol

通常,当将对象(例如,
main menu
)向上转换到基类或协议(在您的例子中是
menuptocol
)时,编译器只能访问该协议提供的属性/函数。这一限制是由于您也可能传递符合协议的其他对象,但在您的情况下会丢失所有其他属性,如
id

在您的示例中,我看不到为菜单使用面向协议的实现的理由,因为您没有以多态方式使用
main菜单
SubMenu1
。当涉及到协议、泛型和继承时,我的建议是:在没有这些特性的情况下,尽量保持它的简单性,如果没有这些特性就无法解决问题,则添加这些特性

关于面向协议编程的优秀WWDC视频和播客
ForEach
需要一个
可识别的
类型,这就是为什么您的菜单*-类型符合
可识别的
。但是传递给
ForEach
的是一个不符合
可识别的
MenuProtocol

通常,当将对象(例如,
main menu
)向上转换到基类或协议(在您的例子中是
menuptocol
)时,编译器只能访问该协议提供的属性/函数。这个限制是