Think & Build

img_feature-2

Building custom UI Elements with IBDesignable

If you have read some of my previous articles you’ve probably noticed that I love UI customization! It’s an excellent chance to let your App stand out from the crowd thanks to its unique looks and innovative interactions.
It looks like Apple really wants us to unleash our creativity and they are proving it with a couple of useful additions that come with Xcode 6 and iOS 8: IBDesignable is one of them.

BDESIGNABLE AND IBINSPECTABLE

These two new keywords have been added to Xcode 6 to preview our custom views directly through Interface Builder. That’s right: this is a dramatic improvement in terms of re-usability, sharing, and, in a way, testing.

We prefix our class with the @IBDesignable keyword to inform Interface Builder that the class’ instances will try to design themselves when added to the storyboard:

@import UIKit
@IBDesignable class ACustomView UIView {

Then, prefixing any properties of the class with @IBInspectable, we ensure that Interface Builder can read and write the value of these properties directly in the inspector view:

@IBInspectable var color: UIColor = UIColor.clearColor() { 
    didSet{ 
      updateUI()
    }
}

The previous code will end up returning new fields in Interface Builder to setup that property:

and in the ViewController in the storyboard we get a preview of the view like for any other UIKit component:

LET’S CODE

To learn more about @IBDesignable we are going to implement a custom view with some interesting features:

1) a two-step gradient as background
2) the gradient can be oriented horizontally or vertically
3) the view can have rounded corners

And here is how the view looks and how we interact with it through Interface Builder:

As you can see we can modify its IBinspectable features directly by Interface Builder updating the view in realtime.

Before implementing the class, however, there is an important thing to remember: IBInspectable works only through frameworks . With that said, we have to implement the gradient view within a framework. (Update September 2014: This is no more needed and you can skip the next section “Building your framework”. I still prefer encapsulating my custom controls in Frameworks to easily share them between my projects… so I suggest you to read it anyway ;) )

BUILD YOUR FRAMEWORK

Fear not, creating a framework is extremely simple. By the way, I suggest you to take a look at WWDC2014 session 416 “Building modern Frameworks” video if you want complete info about how frameworks work. Suffice to say that with a framework you can encapsulate code and resources into a single container and easily share it with extensions and other applications.

To create the framework go to:
- File->new->Target
and select from the left panel
- iOS->Framework&Library
- Now select Cocoa Touch Framework

Now just choose a name for your Framework, I’ve used BWGradientView don’t edit the other options, just be sure to use Swift as language.

You should end up with a new folder in the project named as your framework and with a new target.

Add a new Cocoa Touch class to this folder (file->new->file / iOS->source->Cocoa Touch class). Set UIView as “subclass of” and again select Swift as language. I’ve named this class BWGradientView (I’ve chosen to use the same name of the framework).

IMPLEMENTING THE CLASS

The code for this class is extremely simple. Setting the main layer of the view as CAGradientLayer is going to help us draw the gradient with little effort, then we need a couple of properties to achieve the goals we previously listed.

First, we have to set the class as @IBDesignable

@IBDesignable class BWGradientView: UIView { 

Then add the properties implemented with the didSet observer and marked as @IBInspectable so that they can be accessed by Interface Builder.

    // MARK: Inspectable properties ******************************

    @IBInspectable var startColor: UIColor = UIColor.whiteColor() {
        didSet{
            setupView()
        }
    }

    @IBInspectable var endColor: UIColor = UIColor.blackColor() {
        didSet{
            setupView()
        }
    }

    @IBInspectable var isHorizontal: Bool = false {
        didSet{
            setupView()
        }
    }

    @IBInspectable var roundness: CGFloat = 0.0 {
        didSet{
            setupView()
        }
    }

The setupView function is where all the drawing takes place.

    // MARK: Internal functions *********************************

    // Setup the view appearance
    private func setupView(){

        let colors:Array = [startColor.CGColor, endColor.CGColor]
        gradientLayer.colors = colors
        gradientLayer.cornerRadius = roundness

        if (isHorizontal){
            gradientLayer.endPoint = CGPoint(x: 1, y: 0)
        }else{
            gradientLayer.endPoint = CGPoint(x: 0, y: 1)
        }

        self.setNeedsDisplay()

    }

    // Helper to return the main layer as CAGradientLayer
    var gradientLayer: CAGradientLayer {
        return layer as CAGradientLayer
    }

Nothing really complex. It just sets the gradient’s color, direction and cornerRadius depending on the previously described properties.
This function is called as soon as one of the previous properties gets modified.

Just for the sake of completeness here you can find the init functions and the layerClass override to setup the main layer as a CAGradientLayer.

    // MARK: Overrides ******************************************

    override class func layerClass()->AnyClass{
        return CAGradientLayer.self
    }

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

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

Done!

INTERFACE BUILDER

Now just open the storyboard, add a new UIView in the root controller and set its class to BWGradientView (or whatever you have chosen as class name). In the Identity inspector you should hopefully see a green dot near the label “Designables” with the message: “Up to date”.

Your view should now present a black and white gradient.
Let’s add a bit of color to the GradientView. Open the attribute inspector and you’ll see a box with all the properties that we have set as @IBInspectable. You can modify them to obtain a more interesting effect and you can see your view getting updated in real time within the storyboard:

Mission accomplished!

Download Source