简体   繁体   English

使用 SwiftUI ForEach 迭代[任何协议],其中所述协议是可识别的

[英]Using SwiftUI ForEach to iterate over [any Protocol] where said protocol is Identifiable

In a ViewModel I have:在 ViewModel 我有:

public var models: [any Tabbable]

And Tabbable starts with Tabbable 以

public protocol Tabbable: Identifiable {
   associatedtype Id
   var id: Id { get }
   /// ...
}

In Swift in my ViewModel I can use the Array.forEach to:在我的 ViewModel 中的 Swift 中,我可以使用 Array.forEach 来:

models.forEach { _ in
   print("Test")
}

But in SwiftUI I can't use ForEach to:但在 SwiftUI 中,我不能使用 ForEach 来:

ForEach(viewModel.models) { _ in
   Text("Test")
}

Due to:由于:

Type 'any Tabbable' cannot conform to 'Identifiable'类型 'any Tabbable' 不能符合 'Identifiable'

Any suggestions?有什么建议么? This is with Swift 5.7.这是 Swift 5.7。 Do I need to go back to making something like AnyTabbable?我需要回去制作像 AnyTabbable 这样的东西吗? Thanks in advance.提前致谢。

Consider this example in the form of a Playground:以 Playground 的形式考虑这个例子:

import UIKit
import SwiftUI

public protocol Tabbable: Identifiable {
    associatedtype Id
    var id: Id { get }
    
    var name : String { get }
}

struct TabbableA : Tabbable{
    typealias Id = Int
    
    var id : Id = 3
    var name = "TabbableA"
}

struct TabbableB : Tabbable {
    typealias Id = UUID
    
    var id : Id = UUID()
    var name = "TabbableB"
}

struct ViewModel {
    public var models: [any Tabbable]
}

let tabbableA = TabbableA()
let tabbableB = TabbableB()
let models: [any Tabbable] = [tabbableA, tabbableB]

struct ContentView {
    @State var viewModel : ViewModel = ViewModel(models: models)
    
    var body : some View {
        ForEach(viewModel.models) { model in
            Text(model.name)
        }
    }
}

In this case, we have the type TabbableA where each instance has an id property that is an integer (for the sake of the sample they only use "3" but it's the type that is significant).在这种情况下,我们有类型TabbableA ,其中每个实例都有一个整数id属性(为了示例,他们只使用“3”,但它是重要的类型)。 In TabbableB each instance's id is a UUID.TabbableB每个实例的id都是一个 UUID。 Then I create an array where one item is an instance of TabableA and another is an instance of TabbableB .然后我创建一个数组,其中一项是TabableA的实例,另一项是TabbableB的实例。 The array is of type [any Tabbable] .该数组的类型为[any Tabbable]

Then I try to use ForEach on the array.然后我尝试在数组上使用ForEach But some elements in the array use Int ids and some use UUID ids.但是数组中的一些元素使用Int id,一些使用UUID id。 The system doesn't know what type to use to uniquely identify views.系统不知道使用什么类型来唯一标识视图。 Ints and UUIDs can't be directly compared to one another to determine equality. IntsUUIDs不能直接相互比较以确定相等性。 While each item that went into the array is Tabbable , and therefore conforms to Identifiable , the elements coming out of the array, each of which is of type any Tabbable , do not conform to Identifiable .虽然进入数组的每个项目都是Tabbable ,因此符合Identifiable ,但从数组中出来的元素(每个元素的类型any Tabbable符合Identifiable So the system rejects my code.所以系统拒绝了我的代码。

You can provide a KeyPath to the id (or any unique variable): parameter which specifies how to retrieve the ID in ForEach .您可以为 id(或任何唯一变量)提供 KeyPath:参数,该参数指定如何在ForEach中检索ID It is because all items in ForEach must be unique.这是因为ForEach中的所有项目都必须是唯一的。 You can create a structure, which conforms to your protocol.您可以创建一个符合您的协议的结构。 At that moment it should work.在那一刻它应该工作。 The second option is to remove Identifiable from the protocol and then you can use that directly.第二个选项是从协议中删除Identifiable ,然后您可以直接使用它。

    public protocol Tabbable {
        var id: String { get }
        /// ...
    }
    
    public var models: [Tabbable]

    ForEach(viewModel.models, id: \.id) { _ in
       Text("Test")
    }

or或者

    public protocol TabbableType: Identifiable {
        associatedtype Id
        var id: Id { get }
        /// ...
    }

    struct Tabbable: TabbableType {
        var id: String { get }
        /// ...
    }

    public var models: [Tabbable]

    ForEach(viewModel.models) { _ in
       Text("Test")
    }

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

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