IBDesignable and IBInspectable in Interface Builder
A picture is worth a thousand words. Simple and meaningful. Why should we setup whole view (meaning not only UIView
, but all its subclasses including buttons, images or cells) with raw code and imagine how it possibly could look, when you can easily use IBInspectable
and IBDesignable
available in Interface Builder since Xcode 6? Exactly! Let’s try it out and take a look at how simple it is.
First, as usual, create a brand new Xcode project. I’ve chosen Swift, but if you prefer Objective-C it’s almost the same and I promise I am going to point out the most important differences.
As far as I remember it’s not that rare of a case when we need to create a custom UIView
that has rounded corners and is bordered. It’s quite easy from code, but imagine that the same UIView
could be configurable – in scenario one it is rounded with bold black border, but in scenario two it is perfect round circle with thin green border. And in scenario three it has no border! Quite uncomfortable from the source code (but still possible) – probably you are going to need a custom initializers, but that still does not let you see your effort in Interface Builder. IBInspectable
and IBDesignable
to the rescue!
To use it just create our new UIView
class. Choose File > New > File… (or just ⌘N) and then iOS > Source > Cocoa Touch Class. I named mine PrettyView and made it UIView
subclass. We don’t need drawRect(_:)
here, so begin by removing it. We need three properties that let us configure the view in IB: corner radius, border color and border width. Add them and then your class should look like this:
1 2 3 4 5 6 7 8 9 | import UIKit class PrettyView: UIView { var cornerRadius: CGFloat = 0.0 var borderColor: UIColor = .clearColor() var borderWidth: CGFloat = 0.0 } |
I set them up with initial, neutral values. I believe that you still remember how it should look like in Obj-C, especially if you have chosen this language, so I won’t place a sample code here. You may consider why I prefer CGFloat
than modern Double
– it’s because we are going to modify layer’s properties, like cornerRadius
, being CGFloat
s. Using this type with these variables makes typecasting unnecessary.
It’s right time to add some keywords to the code. IBDesignable
is connected with classes (making them designable) and IBInspectable
with variables (making them inspectable and visible in IB). Do not hesitate and add them to proper places.
1 2 3 4 5 6 7 8 9 10 | import UIKit @IBDesignable class PrettyView: UIView { @IBInspectable var cornerRadius: CGFloat = 0.0 @IBInspectable var borderColor: UIColor = .clearColor() @IBInspectable var borderWidth: CGFloat = 0.0 } |
In Objective-C keywords are IB_DESIGNABLE
and IBInspectable
.
Right now these properties should be visible in IB when you add the subclassed UIView
to your UIViewController
. However it cannot be rendered properly, because Interface Builder does not know what to do with these variables. We should implement layoutSubviews()
and set layer
properties there.
1 2 3 4 5 6 7 | override func layoutSubviews() { super.layoutSubviews() layer.cornerRadius = cornerRadius layer.borderColor = borderColor.CGColor layer.borderWidth = borderWidth layer.masksToBounds = true } |
Instead of this you can set layer
‘s properties in didSet
of each @IBInspectable variable
. I prefer this way, because the resulting outcome is the same, but we use a more modern, swift approach. Unfortunately, that is not so clear in Objective-C and requires custom setters. Although I prefer Swift computed properties than C-style setters and getters. Let’s rewrite the code in Swift.
In the end PrettyView.swift could look like this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import UIKit @IBDesignable class PrettyView: UIView { @IBInspectable var cornerRadius: CGFloat = 0.0 { didSet { layer.cornerRadius = cornerRadius } } @IBInspectable var borderColor: UIColor = .clearColor() { didSet { layer.borderColor = borderColor.CGColor } } @IBInspectable var borderWidth: CGFloat = 0.0 { didSet { layer.borderWidth = borderWidth } } override func layoutSubviews() { super.layoutSubviews() layer.masksToBounds = true } } |
Last step is to add our UIView
to UIViewController
‘s UIView
. Open Main.storyboard and then drag and drop a view. Next change its class in Identity Inspector (⌥⌘3) to PrettyView
. Then you should find three new properties in Attributes Inspector (⌥⌘4).
All changes should be almost immediately visible in Interface Builder. It can take some time if you have really heavy view, but I believe it is worth it.
That is all for today. You can add more IBInspectable
variables to your class or find new ways in different classes.
You can find complete source code on Droids on Roids’s GitHub repository.
About the author
Ready to take your business to the next level with a digital product?
We'll be with you every step of the way, from idea to launch and beyond!