How to visualize reusable xibs in storyboards using IBDesignable
By: Adrien Cognée
Tags: Storyboard, iOS, Zenchef, Xcode, Swift

At Zenchef we use storyboards to design view controllers and get an overview of app navigation.

However storyboards are not great for views you want to use multiple times. For example, you may want to use a complex view in a UIViewController, in a UITableViewCell or in a UICollectionViewCell.

To avoid designing your view every time, you usually have 2 solutions

1. Design your view programmatically

This way you can make your view IBDesignable and by implementing prepareForInterfaceBuilder you’ll see the result in your storyboards when you include this view.

It’s easy and great when your view implements drawRect using CoreGraphics.

However as soon as you get many subviews, this becomes unpractical. Coding autolayout constraints can be painful, size class even more. Interface Builder is powerful and we want to use it as much as we can.

2. Design your view in a Xib

This way you can enjoy all the features of Interface Builder for auto layout, size class and trait collection while keeping your view reusable.

However, every time you need it, you have to load your xib and add the view to your view hierarchy programmatically. Also, you can’t benefit from Live Views (introduced in Xcode 6). You won’t visualize your xib in your storyboards because at this stage, all IBOutlet are nil. It will fail to render and the view debugger will give you the following error:

unexpectedly found nil while unwrapping an Optional value

3. Alternative

So here is a way to include and visualize xibs in storyboards:

Step 1 — Create a view container

We need to create a utility class behaving like a container we call XibView.

Our XibView will be responsible for loading our xib and add our view as its subview.

It has 2 attributes:

contentView will be the view you created in your xib.

nibName is the title of your xib file. We want this field accessible from the identity inspector later on in our storyboard.

@IBDesignable

class XibView : UIView {



var contentView:UIView?

@IBInspectable var nibName:String?

On initialization the class will:

load view from its xib name

add as subview with autoresizing mask for autolayout constraints

override func awakeFromNib() {

super.awakeFromNib()

xibSetup()

}



func xibSetup() {

guard let view = loadViewFromNib() else { return }

view.frame = bounds

view.autoresizingMask =

[.flexibleWidth, .flexibleHeight]

addSubview(view)

contentView = view

}



func loadViewFromNib() -> UIView? {

guard let nibName = nibName else { return nil }

let bundle = Bundle(for: type(of: self))

let nib = UINib(nibName: nibName, bundle: bundle)

return nib.instantiate(

withOwner: self,

options: nil).first as? UIView

}

Step 2 — Setup xib’s « File’s Owner »

In your xib, setup the File’s Owner with the utility class you created. It doesn’t require any other changes in your xib.

Step 3 — Add xib to storyboard

In your storyboard, when you want to use your view,

set its class as XibView

set it’s nibName property as your xib filename in the identity inspector

Step 4 — Visualize xib in storyboard

As you can see, you are still not able to visualize the xib in your storyboard. So edit your XibView and add the following:

override func prepareForInterfaceBuilder() {

super.prepareForInterfaceBuilder()

xibSetup()

contentView?.prepareForInterfaceBuilder()

}

This will instantiate your view as seen previously and trigger IB rendering (make sure it is IBDesignable too!)

Finally

This way (by repeating step 3) you can easily add xib to your storyboard and see them as Live Views. It’s pretty straightforward. It doesn’t have a huge impact on your code. It simply encapsulates your view.