Embedding External Nibs for Fun and Profit

Here's a common use case we run across. We're building a really nice custom control, and it's got a reasonably complex view hierarchy. We want to be able to have multiple copies of it, plus keep the complexity of the higher-level UI down, so we store it in a nib.

The only way you can get stuff out of a nib is to use the UINib API to load the nib, get the loaded object, and then start using it. This means writing code. We want to keep the amount of code in the app down, and programatically generating interfaces sucks. What we want to do is to just insert a UIView placeholder into our view controller, and have it be replaced with the full control at runtime.

So, how can we do this?

Some previous work on this topic was done by Yang Meyer, who figured out that you can override the -awakeAfterUsingCoder: method to switch out the placeholder view with a view loaded from a nib at load-time. However, this method doesn't play nice with ARC.

We came up with a solution that we quite like. It's easy to understand, allows us to keep the control's UI in a separate nib, and also allows us to simply insert an empty UIView into our view controllers (and not clutter them up).

We used this technique in the development of SLNumberPickerView.

The Technique

First, design the interface for your class in a separate nib. We find it helpful to write a class method that loads and returns the object:

Next, we override -awakeAfterUsingCoder: to check to see if self is a placeholder view or the real view that was loaded from the external nib. We determine this based on how many subviews we have - if it's zero, then we're the empty placeholder view.

If we figure out that self is the placeholder, we load a new instance of the view, and then add it as a subview of the placeholder. We also keep a reference to this internal view, and forward any relevant messages to it.

There's one drawback to this technique: because we're inserting the real view inside the placeholder view, we're keeping an extra instance of the class around (though none of its subviews are loaded, so not too much additional memory is allocated.)

You can see an example of this technique in action in SLNumberPickerView, available on our GitHub.