ios - `Identifiable` 一致性返回类型泛型作为变量

标签 ios swift swiftui

背景:

我正在尝试根据 Apple 的此示例构建一个 OutlineGroup:https://developer.apple.com/documentation/swiftui/outlinegroup

但是我不想使用静态类型,而是想使用符合我的协议(protocol)的对象。因此,如果我想做的事情可以很快实现,那么问题就更普遍了:

问题:

我有一个协议(protocol),可以为我提供来自各种对象的同事姓名:

protocol CastProtocol {
    var name: String { get }
    var coworkers: [CastProtocol]? { get } // here will be the build error later
}

并以两个模型为例:

class Actor: Identifiable, CastProtocol {
    var id: Self { self }
    var name: String { return "Chuck Norris" }
    
    var coworkers: [CastProtocol]? {
        return [
            Director()
        ]
    }
}

class Director: Identifiable, CastProtocol {
    var id: Self { self }
    var name: String { return "Gina Carey" }
    
    var coworkers: [CastProtocol]? {
        return [
            Actor(),
            Director(),
            Actor()
        ]
    }
}

到目前为止,一切正常,但我需要确保我的 CastProtocol 符合 Identifying,因此将我的协议(protocol)实现更改为:

protocol CastProtocol: Identifiable {
    ...
}

现在这产生了一个问题:

Protocol 'CastProtocol' can only be used as a generic constraint because it has Self or associated type requirements

问题:

  1. 那么有一种方法可以通过在 coworkers 中返回非静态类型来实现此目的,或者也许我应该使用子类化?
  2. 这是正确的方法吗?也许我唯一缺少的是在协议(protocol)级别标识符要求中声明可识别?但如果是这样 - 怎么办?
  3. 我认为我真的不能选择关联类型。如果此示例中的 coworkers 可以包含 ActorDirector 关联类型,我认为无法使用

最佳答案

协议(protocol)从不遵守其他协议(protocol)。语法 protocol CastProtocol: Identifying 并不意味着“CastProtocol 符合 Identifying”。它的意思是“为了符合 CastProtocol,类型也必须符合 Identifying。”

对于这个问题,不,你不能要求这样做。 可识别包括关联的类型ID。考虑这段代码:

let coworker in coworkers {
    let id = coworker.id // What type is `id` here?
    ...
}

在这种情况下,Swift 无法分配单一类型 id。有一天,他们可能会添加一个称为“广义存在”的功能,该功能将允许将 id 推断为 Any where Self: Hashable 类型,但目前还无法用以下方式表达这一点: swift 。

但就你的情况而言,无论如何它都无法工作。说你能做到这一点。由于您已将标识符设置为对象本身,因此系统最终需要评估Director == Actor。这在 Swift 中是不可能的。它们不是同一类型。

相反,我怀疑你想要这个,如果你真的希望 Actor 成员成为类:

protocol CastMember: class {
    var name: String { get }
    var coworkers: [CastMember] { get } // Optional Arrays are almost always wrong
    var id: ObjectIdentifer { get }
}

extension CastMember {
    var id: ObjectIdentifier { ObjectIdentifier(self) }
}

// You can still put Identifiable here for [Actor] properties
class Actor: CastProtocol, Identifiable {
    // ...
}

现在,这不会让您将它们直接放入 ForEach(actor.coworkers) 调用中,我认为这就是您想要的。就我个人而言,我只是传递 id:\.id 参数,但是如果您经常这样做,您当然可以使用扩展使其变得更好一点:

extension ForEach where Content : View, Data.Element == CastMember {
    public init(_ data: Data, @ViewBuilder content: @escaping (Data.Element) -> Content) {
        self.init(data, id: \.id, content: content)
    }
}

请注意 Data.Element == CastMember 而不是 :。这适用于 CastMember 数组,而不是符合 CastMember 的事物数组。

关于ios - `Identifiable` 一致性返回类型泛型作为变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63192951/

相关文章:

ios - ios objective c中的内存管理

ios - 编辑属性字符串时会调用哪种替换方法?

SwiftUI 列表禁用单元格按下

ios - 错误 : failed to import bridging header when trying to Archive

iOS:使用 iCloud 文档存储小型基于 XML 的数据库

ios - 将 json 中的图像数组放入 CollectionView 自定义单元格 swift4

swift - 如何制作一个在大小变化时遵循圆形路径的 Sprite ?

ios - 如何在调用某人时删除 iOS 应用程序中的弹出窗口?

swift - 如何在 VStack 中为 subview 排列底部文本

swift - ForEach 不必要地更新 View