简体   繁体   中英

Type any Protocol cannot conform to Protocol

I am facing an issue when trying to initialize a view in SwiftUI. Let me explain:

I have a view called ExternalView, this view has a constraint with a protocol called ToggleableProtocol, then, this view internally consumes a view called InternalView, which is a view that has a constraint to a Hashable protocol.

The error occurs when I try to create an instance of ExternalView and pass it an array of different structs that conform to the ToggleableItem protocol. The error says Type 'any TogleableItem' cannot conform to 'TogleableItem'

TogleableItem

public protocol TogleableItem: Hashable {
    var text: String { get }
    var isSelected: Bool { get set }
}

Structs conforming to the TogleableItem

struct FirstObject: TogleableItem {
    var text = "FirstItem"
    var isSelected = false
}

struct SecondObject: TogleableItem {
    var text = "SecondItem"
    var isSelected = false
}

ContentView

struct ContentView: View {
    @State private var items: [any TogleableItem] = [
        FirstObject(),
        SecondObject()
    ]
    
    var body: some View {
        ExternalView(
            items: items
        ) { isSelected, index, _ in
            items[index].isSelected = isSelected
        }
    }
}

ExternalView

public struct ExternalView<T: TogleableItem>: View {
    private let items: [T]
    private let onItemTap: (Bool, Int, T) -> Void
    
    public init(
        items: [T],
        onItemTap: @escaping (Bool, Int, T) -> Void
    ) {
        self.items = items
        self.onItemTap = onItemTap
    }
    
    public var body: some View {
        InternalView(
            items: items
        ) { index, element in
            Text(element.text)
                .foregroundColor(element.isSelected ? .red : .blue)
        }
    }
}

InternalView

struct InternalView<Element: Hashable, Content: View>: View {
    private let items: [Element]
    private let content: (Int, Element) -> Content
    
    init(
        items: [Element],
        content: @escaping (Int, Element) -> Content
    ) {
        self.items = items
        self.content = content
    }
    
    var body: some View {
        LazyHStack(spacing: 0) {
            ForEach(items.indices, id: \.self) { index in
                content(index, items[index])
            }
        }
    }
}

Error

Thanks!!

I have tried changing the items parameter, inside the ExternalView to something like [any TogleableItem] but in the end it causes a similar error when consuming the InternalView

The problem with your code is that you define for instance ExternalView as generic

ExternalView<T: TogleableItem>

then you say that T can be of a (read one) type that conforms to TogleableItem but you want to use the view for a mix of types that conforms to TogleableItem .

The solution as I see it is to not make the view types generics and instead use TogleableItem directly in the declarations (I have skipped a lot of code below for brevity)

public struct ExternalView: View {
    private let items: [any TogleableItem]
    private let onItemTap: (Bool, Int, any TogleableItem) -> Void
    ...
}

struct InternalView<Content: View>: View {
    private let items: [any TogleableItem]
    private let content: (Int, any TogleableItem) -> Content
    ...
}

Another way to solve it if InternalView should be able to use other types than those conforming to TogleableItem is to use the original solution but without Element conforming to Hashable

struct InternalView<Element, Content: View>: View {
    private let items: [Element]
    private let content: (Int, Element) -> Content

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM