[英]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
的是一个不符合Identifiable
的MenuProtocol
。
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.在您的示例中,我没有看到为菜单提供面向协议的实现的原因,因为您没有以多态方式使用
MainMenu
和SubMenu1
。 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.