It doesn’t seem possible to create nested custom subviews using .xib’s created with Interface Builder. For example I want to create a custom BarView
and nested inside of that a custom FooView
where the contents of each view is created in IB and stored in
a .xib file:
The BarView
is loaded from its .xib file and added as a subview in the ViewController
‘s viewDidLoad()
method:
class ViewController: UIViewController {
var barView : BarView?
override func viewDidLoad() {
super.viewDidLoad()
self.barView =
(UINib(nibName: "BarView", bundle:Bundle.main).
instantiate(withOwner: nil, options: nil).first as? BarView)
self.view.addSubview(self.barView!)
}
}
I can create and connect to an outlet for the UIButton
created by IB:
class BarView: UIView {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
NSLog("BarView init:NSCoder, barButton set: (barButton != nil)")
}
public override func awakeFromNib() {
super.awakeFromNib()
NSLog("BarView: awakeFromNib(), barButton set: (barButton != nil)")
}
@IBOutlet weak var barButton: UIButton!
}
I can can also connect outlets and actions to the nest FooView
:
class FooView: UIView {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
NSLog("FooView init:NSCoder, fooButton set: (fooButton != nil)")
}
public override func awakeFromNib() {
super.awakeFromNib()
NSLog("FooView: awakeFromNib(), fooButton set: (fooButton != nil)")
}
@IBAction func doFoo(_ sender: Any) {
NSLog("doFoo")
}
@IBOutlet weak var fooButton: UIButton!
}
I can see the init?(NSCoder)
and awakeFromNib()
methods are indeed being called. I don’t know why BarView
‘s method are called
twice:
... BarView init:NSCoder, barButton set: false
... BarView: awakeFromNib(), barButton set: false
... FooView init:NSCoder, fooButton set: false
... BarView init:NSCoder, barButton set: false
... BarView: awakeFromNib(), barButton set: true
... FooView: awakeFromNib(), fooButton set: false
The contents of the FooView
(i.e. the fooButton
) does not show up and is nil
in the FooView
both callbacks. Is nesting custom view’s created with independent .xib files supported? If so, what is the trick to accomplishing this?
The various suggestions in this post are not quite right or don’t work.
2
Answers
Bear in mind that you’re swimming against the current here; storyboards were introduced exactly to get away from having lots of separate .xib files.
They’re called twice because you’re creating two instances. One is in
Main.storyboard
, and the other is inBarView.xib
.This sounds like it’s probably another case of mistaken identity, similar to the problem above. There’s an instance of
Foo
inBarView.xib
that’s different from the one inFooView.xib
.Not directly, as far as I remember. You can certainly load a view from a .xib file and add it to your view graph, though. You could even create a
UIView
subclass whose job is to load the view in a specified .xib and set it as its own subview. One thing you’ll want to pay attention to is the File’s Owner proxy object in the .xib file; be sure to understand how you can use it to connect objects in your .xib file to outlets in the file’s owner.It is certainly possible to embed
FooView.xib
inBarView.xib
— the bigger question, though, is: why?Here’s a quick example…
FooView.xib
FooView.xib source
BarView.xib
BarView.xib source
Storyboard
Storyboard source
Couple "helper" extensions
FooView class
BarView class
When running, it looks like this:
Tapping either button will call the
@IBAction
func (in the associated class) and output to the debug console:Note that we can replicate that via code, rather than Storyboard.
This, with an empty view controller, will give you the exact same result:
We can mark these as
@IBDesignable
— however, from my experience, Xcode is pretty sketchy when it comes to that. Frequently get "error: IB Designables: Failed to render and update auto layout status for UIView…"As a general rule, embedding a xib in a xib in a xib etc is just asking for trouble. You end up with absurdly tightly coupled elements which will be prone to errors.
If you have a legitimate reason for doing so, great, it’s your code / project.