简体   繁体   English

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

[英]Swift Value of protocol type 'XXX' cannot conform to 'Identifiable'; only struct/enum/class types can conform to protocols

I'm getting the following error:我收到以下错误:

Value of protocol type 'MenuProtocol' cannot conform to 'Identifiable'; only struct/enum/class types can conform to protocols

I have a main menu which some of its options have a submenu.我有一个主菜单,其中一些选项有一个子菜单。 I decided to use an enum for the main menu, and another enum for any submenus.我决定对主菜单使用一个enum ,对任何子菜单使用另一个enum All of the enums implement a protocol I defined, which allows me to specify the text that I'm going to show for each menu option.所有枚举都实现了我定义的协议,它允许我指定要为每个菜单选项显示的文本。

I'm not sure if this is the best approach, but I found it useful.我不确定这是否是最好的方法,但我发现它很有用。

Any ideas how can I fix this error?任何想法如何解决此错误? Thanks for your help!谢谢你的帮助!

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)
        }
    }
}

ForEach expects an Identifiable type, that's why your Menu*-types conform to Identifiable . ForEach需要一个Identifiable类型,这就是您的 Menu*-types 符合Identifiable的原因。 But what is passed to ForEach is a MenuProtocol that doesn't conform to Identifiable .但是传递给ForEach的是一个不符合IdentifiableMenuProtocol

In general, when upcasting an object (eg MainMenu ) to a base-class or protocol (in your case MenuProtocol ), the compiler is only able to access the properties/functions that are provided by that protocol.通常,当将 object(例如MainMenu )向上转换为基类或协议(在您的情况下MenuProtocol )时,编译器只能访问该协议提供的属性/函数。 This limitation is due the fact that you may also pass other objects that conform to the protocol but miss all the other properties like the id In your case.此限制是由于您还可以传递其他符合协议的对象,但在您的情况下会错过所有其他属性,例如id

In your example I don't see a reason for having a protocol-oriented implementation for the Menu, because you don't use MainMenu and SubMenu1 in a polymorphism way.在您的示例中,我没有看到为菜单提供面向协议的实现的原因,因为您没有以多态方式使用MainMenuSubMenu1 My suggestion when it comes to protocols and generics and inheritance: try to keep it as simple as possible without any of those features and add those if you cannot solve your problem without.我对协议和 generics 和 inheritance 的建议是:尽量在没有这些功能的情况下使其尽可能简单,如果没有这些功能就无法解决问题,请添加这些功能。

Good WWDC video and podcast about protocol-oriented programming https://developer.apple.com/wwdc15/408 https://www.swiftbysundell.com/podcast/71/关于面向协议编程的优秀 WWDC 视频和播客https://developer.apple.com/wwdc15/408 https://www.swiftbysundell.com/podcast/71/

You can use indices and retrieve your data using an index.您可以使用indices并使用索引检索数据。 Just change your HStack .只需更改您的HStack

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 协议类型'*'的值不能符合'*'; 只有结构/枚举/类类型可以符合协议 - Value of protocol type '*' cannot conform to '*'; only struct/enum/class types can conform to protocols 协议类型“Encodable”的值不能符合“Encodable”; 只有结构/枚举/类类型可以符合协议 - Value of protocol type 'Encodable' cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols 协议类型“Any”的值不能符合“Equatable”; 只有结构/枚举/类类型可以符合协议 - Value of protocol type 'Any' cannot conform to 'Equatable'; only struct/enum/class types can conform to protocols Swift - 类型“(字符串,JSON)”不能符合“字符串协议”; 只有结构/枚举/类类型可以符合协议 - Swift - Type '(String, JSON)' cannot conform to 'StringProtocol'; only struct/enum/class types can conform to protocols 类型“MovieSearchContainer.Type”不能符合“Decodable”; 只有结构/枚举/类类型可以符合协议 - Type 'MovieSearchContainer.Type' cannot conform to 'Decodable'; only struct/enum/class types can conform to protocols 类型 &#39;()&#39; 不能符合 &#39;View&#39;; 只有 struct/enum/class 类型可以符合使用 swift ui 调用调用函数的协议 - Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols calling calling functions with swift ui Swift 错误:“类型 &#39;()&#39; 不能符合 &#39;View&#39;;只有 struct/enum/class 类型可以符合协议”调用函数写入文本时 - Swift error: "Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols" when calling function to write text 使用 if 语句时:类型 '()' 不能符合 'View'; 只有结构/枚举/类类型可以符合协议 - While using an if statement: Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols 类型 &#39;() -&gt; ()&#39; 不能符合 &#39;View&#39;; 只有结构/枚举/类类型可以符合协议 - Type '() -> ()' cannot conform to 'View'; only struct/enum/class types can conform to protocols navigationBarItems“类型[视图]不能符合'视图'; 只有结构/枚举/类类型可以符合协议” - navigationBarItems “Type [view] cannot conform to 'View'; only struct/enum/class types can conform to protocols”
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM