如何允许子 classes 使用父 class 的笔尖

How to allow subclasses to use parent class's nib

假设我有一个父视图 class,它至少包含 1 个 属性:

  class BaseView : UIView {
    @IBOutlet weak var myLabel: UILabel!
  } 

这个 class 有一个对应的 xib 文件,带有从 xib 到 myLabel 的出口连接 属性。

现在假设我们还有一些子 class 继承自此 class:

  class ChildView : BaseView {
     func setup() {}
  }

ChildView 有一些自定义逻辑,但可以重复使用 BaseView 中的所有视图。它没有(或者我宁愿避免它有)自己对应的 xib 文件。

我希望能够做这样的事情:

  let childView = Bundle.main.loadNibNamed(String(describing: BaseView.self), owner: nil, options:nil)?.first as! ChildViewA

但这不起作用。也没有:

  let childView = ChildView()
  Bundle.main.loadNibNamed(String(describing: BaseView.self owner: childView, options: nil)

有没有办法让子视图以类似的方式从其父视图的 xib 文件继承?

问题在于 nib 中的根视图是 BaseView 类型,因此 as! ChildViewA 失败。由于您无权访问 nib 加载器用于取消存档 xib 的 NSKeyedUnarchiver,因此在取消存档期间没有简单的方法来替换您自己的 class。

这里有一个解决方法。

不要不要BaseView本身嵌入到xib中。相反,将 xib 中的顶级视图设为普通 UIView,并将文件所有者自定义 class 设置为 BaseView。然后删除与顶级视图的所有连接,并将它们设置在文件所有者上。还给 BaseView 一个 rootViewFromNib 插座,并将其连接到根视图。

然后,给 BaseView 一个加载其 nib 的初始化程序并将 rootViewFromNib 作为子视图添加到自身,其框架固定到 BaseView 自己的 bounds.您可以使用自动调整大小来做到这一点。

最后,BaseView应该是这样的:

class BaseView: UIView {

    @IBOutlet var myLabel: UILabel!
    // other outlets, etc.

    @IBOutlet private var rootViewFromNib: UIView!

    override init(frame: CGRect) {
        super.init(frame: frame)

        Bundle(for: BaseView.self).loadNibNamed("BaseView", owner: self, options: nil)
        rootViewFromNib.frame = bounds
        rootViewFromNib.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        rootViewFromNib.translatesAutoresizingMaskIntoConstraints = true
        addSubview(rootViewFromNib)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

BaseView.xib应该是这样的: