这是场景。我想要显示许多不同的视图,具体取决于我显示用户的模型对象。所以我已经建立了一个协议,可以呈现实现它的任何视图。
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
}
}
然后,当我在视图控制器中使用它们时,我已经获得了一个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 */
}
}
除了最后一行之外,一切都有效,我尝试调用UIView
方法(removeFromSuperview
)。这并不奇怪,因为ItemView
与UIView
无关。
在Objective C中,我会通过指定我的itemView ivar来解决这个问题:
@property (nonatomic, strong) UIView<ItemView> *itemView;
但我似乎无法为Swift找到类似的语法。我怀疑在Swift中没有办法使用这种模式。如何以Swift友好的方式实现可互换UIView类的总体目标?
到目前为止我找到的一个 hacky 解决方案是将我调用的任何UIView
方法(例如removeFromSuperview
)添加到我的ItemView
协议中
我从Maurice Kelly获得的另一个建议是UIView
实现ItemView
协议,SpecificItemView
和{{1}可以从...下降。你可以看到它implemented in this gist。虽然这解决了在单一类型(例如AnotherItemView
)中封装类AND协议的问题,但它基本上使协议毫无意义,因为您现在正在实现父类中的所有协议方法,在子类中重写它们。此解决方案的最大缺点是,当您在View Controller中引用它们时,必须将子类的实例(var itemView: ItemViewParentClass
)强制转换为新的hypthetical父类(SpecificItemView
)。
答案 0 :(得分:1)
正如您所发现的,没有办法为对象指定类和协议。
解决问题的最简单方法是在协议中添加removeFromSuperview
(以及保证所需的任何其他UIView
方法)。
答案 1 :(得分:1)
从Swift 5开始,可以通过将协议限制为仅应用于UIViews
来实现这一点。为此,只需执行以下操作:
protocol ItemView: UIView {
func setupItem(item: MyItem)
}
通过将此约束添加到协议中,迅速编译器识别为ItemView
的任何对象也将具有所有UIView
方法。
类似地,如果非UIView
子类尝试实现ItemView
协议,则您的代码将无法编译。
答案 2 :(得分:0)
也许以下(在Swift 4中)可以做到这一点?
class MyViewController: UIViewController {
typealias T = UIView & ItemView
var itemView: T?
}
答案 3 :(得分:-1)
ItemView
不会延伸UIView
,但您的SpecificItemView
会延长removeFromSuperView
。当然,ItemView
上不存在ItemView
。
在ViewController中,您声明了SpecificItemView
类型对象,而不是MyViewController
将您的var itemView:SpecificItemView?
代码更改为:
{{1}}