簡體   English   中英

任何 Identifiable 都不能符合“Identifiable”

[英]any Identifiable can't conform to 'Identifiable'

更新:添加關於 Hashable 的相同錯誤


我創建了一個可Identifiable的兼容協議和兼容結構。 然后,當我創建列表並在ForEach中引用它時,我收到錯誤Type 'any TestProtocol' cannot conform to 'Identifiable' (我收到關於Hashable的相同錯誤)。

我應該如何修復這個程序?

如果我寫ForEach(list, id: \.id) ,它可以工作,但我認為符合 Identifiable 沒有意義。

import SwiftUI

protocol TestProtocol: Identifiable, Hashable {
    var id: UUID { get set }
    var name: String { get set }
    
    func greeting() -> String
    static func == (lhs: Self, rhs: Self) -> Bool
}

extension TestProtocol {
    static func == (lhs: Self, rhs: Self) -> Bool {
        return lhs.id == rhs.id
    }
}

struct Person: TestProtocol {
    var id = UUID()
    var name: String
    
    func greeting() -> String {
        return "my name is \(name) and I'm a human."
    }
}

struct Dog: TestProtocol {
    var id = UUID()
    var name: String
    
    func greeting() -> String {
        return "my name is \(name) and I'm a dog."
    }
}

struct ContentView: View {
    var list: [any TestProtocol] = [Person(name: "p1"), Dog(name: "d1")]
    @State var selected: any TestProtocol
    
    var body: some View {
        VStack {
            Picker(selection: $selected) { // Type 'any TestProtocol' cannot conform to 'Hashable'
                ForEach(list) { l in // Type 'any TestProtocol' cannot conform to 'Identifiable'
                    Text(l.greeting()).tag(l) // Type 'any TestProtocol' cannot conform to 'Hashable'
                }
            } label: {
                Text("select")
            }
        }
    }
}

您抱怨Hashable的錯誤消息是“紅色的”。 協議TestProtocol以及所有符合它的結構都符合Hashable

let person = Person(name: "IAmHashable")
print(person.hashValue)

失敗的原因是在Picker中。 它需要具體類型而不是協議。 一種解決方案是創建“容器”類型和處理此問題的自定義綁定。

struct Container: Identifiable, Hashable{
    //implement the same equality as in your TestProtocol
    static func == (lhs: Container, rhs: Container) -> Bool {
        rhs.wrapped.id == lhs.wrapped.id
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(wrapped)
    }
    
    var wrapped: any TestProtocol
    //for convenience
    var id: UUID {wrapped.id}
}

和內容視圖:

struct ContentView: View {
    
    let startArr: [any TestProtocol] = [Person(name: "p1"), Dog(name: "d1")]
    @State private var selected: (any TestProtocol)?

    var body: some View {
        // list of wrapped protocols
        var list: [Container] = { startArr.map{Container(wrapped: $0)}}()
        // binding
        let selectionBinding: Binding<Container> = .init {
            let returninstance = list.first { cont in
                cont.id == selected?.id
            }
            return returninstance ?? list[0]
        } set: { container in
            selected = container.wrapped
        }
        
        // viewCode
        VStack {
            Picker(selection: selectionBinding) {
                ForEach(list) { l in
                    Text(l.wrapped.greeting())
                        .tag(l)
                }
            } label: {
                Text("select")
            }
            // confirmation selection changed
            Text(selected?.name ?? "no Selection")
        }
    }
}

評論:

該解決方案有一些缺點

  • 你的初始數組startArr不應該為空,否則return returninstance?? list[0] return returninstance?? list[0]會破壞你的代碼。 這個是可以處理的,但我認為這超出了這個問題的scope。
  • Container的相等性比較需要與TestProtocol中的相同,因為您無法比較兩個any Protocol s
  • 在選擇某些內容之前,在ContainerView的外觀上selected將為零。 任何用法,例如:在這種情況下的Text元素,都需要處理這個問題。 但是您可以在.onApear中設置它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM