How custom UI controls in Xcode’s Interface Builder boosted my UI development
Technical Mobile Development

How custom UI controls in Xcode’s Interface Builder boosted my UI development

To use or not to use Xcode’s Interface Builder (let’s call it IB) is a very common discussion between iOS developers. There are many arguments against using it like version control issues and difficulties in debugging. This post in our blog have some considerations about it for example. But I believe that if the project is well structured and the team knows how to use it, these issues can be avoided and IB can become a very powerful tool.

Also, IB is being improved each new Xcode version and some of these problems are (slowly) being solved. For example, in the last release (Xcode 9) you can refactor your IBOutlets without  having to change things in IB.

IB is full of tools that help us to improve UI development. For example, since Xcode 4.1 it is easy to set auto layout constraints with a few clicks and draggings, with immediate visual feedback, without the need to run the app to see the results. You can even change this preview to test how UI could be in different screen sizes and orientations.

For me, this resource by itself would already be a good reason to use IB, but of course it can do much more. In this article I’m going to focus on building custom controls with immediate visual feedback using a resource introduced in Xcode 6 called Live Rendering.

Reviewing some basic IB concepts

First of all let’s get in the same page with a small review of some IB concepts.

  • .xib files:

A .xib file is an XML file that is compiled into a .nib file which is copied to the app’s bundle and loaded in runtime to provide UI components (UIView/UIViewController subclasses).

  • .storyboard files:

A .storyboard file is similar to .xib files but it holds a set of UIViewControllers with the possibility of creating relationships between them with segues.

  • @IBOutlet

An IBOutlet is a keyword used to create a reference to an IB object inside your code. These references can be used just like other objects created programmatically.

@IBOutlet weak var loginButton: UIButton!

Creating custom controls

Let’s imagine that your app will contain some repeating components like a rounded button with different corner radius and different borders. Something like the image below:

It’s a good practice to create a custom control and use it everywhere to avoid code repetition. To achieve that using the IB is not trivial as doing by code, but it’s also not a monster. Let’s check it!

The first step is to create a UIButton subview and reference the new control in IB. The class would look something like the following.

import UIKit
class AmazingButton: UIButton {
    var cornerRadius: CGFloat = 2.0 {
        didSet {
            layer.cornerRadius = cornerRadius
            layer.masksToBounds = cornerRadius > 0
        }
    }
}

You can use it programmatically of course. But let’s use it with IB. To do that make sure the selected class in IB object is correct:

To customize the button it would be necessary a reference to the button with an IBOutlet in the UIViewController and then you set the properties in viewDidLoad(). To see the results you just need to run the app.

But running the app may be a slow operation and can become a pain if you only want to see the results of a simple view radius change. And while developing an app we do it a lot.

Here the Live Rendering makes its magic!

To make this property available in IB is very easy. Add @IBDesignable before the class keyword and the @IBInspectable before the properties that you want to make available in IB. The result in the IB is the following:

The properties are now available in the Attributes Inspector of the object and if you change them the results can be seen instantly in the IB. This way you can make adjustments and see the results without having to run the app.

This discussion in StackOverflow lists the types you can expose to IB.

Creating more complex controls using .xib files

Ok, that was easy. But what if you need a more complex kind of custom control? To achieve that you can use a .xib file to create a component made of other components. Let’s create a new one called AwesomeView. This new view contains two AmazingButtons objects arranged in the parent view using Auto Layout with some basic constraints.

Now, let’s create the class that owns this .xib. Let’s call it AwesomeView. This class will have a view that will be a container which will hold the loaded view from the nib file. We can also create IBOutlets for the buttons and other IBInspectable to allow customization directly in IB.

 

import UIKit
@IBDesignable class AwesomeView: UIView {
    @IBOutlet var containerView: UIView!
    @IBOutlet weak var button1: UIButton!
    @IBOutlet weak var button2: UIButton!

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

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initNib()
    }

    func initNib() {
        let bundle = Bundle(for: AwesomeView.self)
        bundle.loadNibNamed("AwesomeView", owner: self, options: nil)
        addSubview(containerView)
        containerView.frame = bounds
        containerView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
    }

    @IBInspectable var button1Title: String = "" {
        didSet {
            button1.setTitle(button1Title, for: .normal)
        }
    }
}

 

Now let’s link the .xib with the class. The trick is to select the File’s owner object in the IB and set the created class for it. So when the class AwesomeView loads this xib it also makes the class the owner of the objects inside the .xib.

 

 

 

 

 

Now you can use the custom control directly in your storyboards or even create it programmatically. To load it in IB you just need to add a view and set the class in the Identity Inspector.

This looks a little confusing, I know. But once you get things working it will be easy to use the custom controls anywhere. To see things working you can check an example project in this link. The project requires Xcode 9.

Conclusion

Interface Builder can be a little tricky sometimes, I agree. It has a complexity that looks like a pain to understand. But on the other hand, if you know it’s superpowers and adopt some rules to keep things organized it can help a lot to increase the team productivity. It is also part of Xcode stack since version 4 so it is being constantly improved with new tools and has Apple support, so it will never get deprecated or discontinued.

Of course there is a learning curve to make the best use of it. But as developers I believe we need to learn our tools just the way we know our code.

About the author

Ricardo Gehrke Filho

I’m a mobile developer who loves giving life to smart and beautiful apps. I also appreciate a good walk at the beach, cold beer, burger and fries, good music and talk about life, the universe and everything else.

Need a team for your projects?
We'd love to hear your ideas!

Connect with us!