Say I have a parent view class, that contains at least 1 property:
class BaseView : UIView {
@IBOutlet weak var myLabel: UILabel!
}
This class has a corresponding xib file with an outlet connection made from the xib to the myLabel property.
Now let's say we also have some child classes that inherit from this class:
class ChildView : BaseView {
func setup() {}
}
ChildView has some custom logic but can reuse all of the views from BaseView. It doesn't (or I'd prefer to avoid it having) its own corresponding xib file.
I'd like to be able to do something like this:
let childView = Bundle.main.loadNibNamed(String(describing: BaseView.self), owner: nil, options:nil)?.first as! ChildViewA
but this doesn't work. Neither does:
let childView = ChildView()
Bundle.main.loadNibNamed(String(describing: BaseView.self owner: childView, options: nil)
Is there anyway to allow a child view to inherit from its parent view's xib file in a similar way?
The problem is that the root view in the nib is of type BaseView
, so as! ChildViewA
fails. Since you don't have access to the NSKeyedUnarchiver
that the nib loader uses to unarchive the xib, there is no easy way to substitute your own class during unarchiving.
Here's a workaround.
Do not embed the BaseView
itself in the xib. Instead, make the top-level view in the xib be a plain UIView
, and set the File's Owner custom class to BaseView
. Then delete all of the connections to the top-level view and set them on the File's Owner instead. Also give BaseView
a rootViewFromNib
outlet, and connect it to the root view.
Then, give BaseView
an initializer that loads its nib and adds that rootViewFromNib
to itself as a subview, with its frame pinned to the BaseView
's own bounds
. You can use autoresizing to do it.
In the end, BaseView
should look like this:
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")
}
}
and BaseView.xib
should look like this: