Swift:属性是实现协议的 UIView 子类

Ken*_*ker 3 polymorphism protocols properties class swift

这是场景。我想要显示许多不同的视图,具体取决于我向用户显示的模型对象。所以我建立了一个协议,任何实现它的视图都可以被呈现。

class MyItem { /* some model properties */ }

protocol ItemView: class {
    // some protocol methods (e.g. updateWithItem(), etc)
    func setupItem(item: MyItem)
}

class SpecificItemView: UIView, ItemView { /* there will be multiple classes like this */
    func setupItem(item: MyItem) {
        // do item setup
    }
}

class AnotherItemView: UIView, ItemView {
    func setupItem(item: MyItem) {
        // do DIFFERENT item setup
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,当我在视图控制器中使用它们时,我得到了一个 ItemView 类:

class MyViewController: UIViewController {

    var itemView: ItemView? // could be a SpecificItemView or AnotherItemView

    override func viewDidLoad() {
        itemView?.setupItem(MyItem())
        itemView?.removeFromSuperview() /* this line won't compile */
    }
}
Run Code Online (Sandbox Code Playgroud)

除了最后一行,我尝试调用UIView方法 ( removeFromSuperview) 之外,一切正常。这并不奇怪,因为ItemView与 没有关系UIView

在 Objective CI 中,将通过指定我的 itemView ivar 来解决这个问题,如下所示:

@property (nonatomic, strong) UIView<ItemView> *itemView;
Run Code Online (Sandbox Code Playgroud)

但我似乎找不到类似的 Swift 语法。我怀疑没有办法在 Swift 中使用这种模式。如何以 Swift 友好的方式实现可互换 UIView 类的总体目标?

到目前为止,我发现的一种巧妙的UIView解决方案是将我调用的任何方法(例如removeFromSuperview)添加到我的ItemView协议中。

我从Maurice Kelly那里得到的另一个建议(不是关于 SO)是创建一个UIView子类,实现ItemView协议,并且SpecificItemView和 都AnotherItemView可以从该子类继承。你可以在这个要点中看到它的实现。虽然这解决了将类和协议封装在单一类型(例如)中的问题var itemView: ItemViewParentClass,但它基本上使协议变得毫无意义,因为您现在在父类中实现所有协议的方法并在子类中覆盖它们。该解决方案的最大缺点是,当您在视图控制器中引用子类 ( SpecificItemView) 的实例时,必须将它们转换为新的假设父类 ( )。ItemViewParentClass

小智 5

从 Swift 5 开始,可以通过将协议限制为仅应用于UIViews. 为此,只需执行以下操作:

protocol ItemView: UIView {
  func setupItem(item: MyItem)
}
Run Code Online (Sandbox Code Playgroud)

通过将此约束添加到您的协议中,swift 编译器识别为 an 的任何对象ItemView也将具有UIView其上可用的所有方法。

同样,如果非子UIView类尝试实现该ItemView协议,您的代码将无法编译。