Building your Custom IBDesignable Controls and Views

February 26, 2015 in

One of the more powerful features introduced with XCode 6 and iOS 8 is the possibility to design, build and integrate custom controls directly in the Interface Builder. Previously, we were able to subclass UIView or any of the UIKit control classes, but we couldn’t really see the results of our customizations until runtime. Now, thanks to IBDesignable controls, we are able to see our custom controls in Interface Builder exactly as they are going to be rendered live, and this really helps a lot when implementing a concrete design for an App.


Making our class IB friendly is surprisingly easy. All we have to do is include the keyword @IBDesignable prior to our class sentence, like this:

import UIKit
@IBDesignable class MyView: UIView {

The IBDesignable attribute tells XCode that MyView will be customizable and designable in Interface Builder.


IBInspectableBut what’s the point of having an IB-customizable view or control if we cannot modify any of its properties? That’s what IBInspectable is for. When applied to a variable, IBInspectable will tell XCode that the value of the variable should be available for modification in the Attribute Inspector of the control in IB. XCode will set up a textfield or selection popup for the control. Currently, the following swift types (and their corresponding Objective-C types) are inspectable: UIImage, UIColor, String, Int, Double, CGFloat, Bool, CGPoint, CGSize and CGRect. These types allow a wide range of customization for your views. Let’s see an example:

@IBDesignable class MyView: UIView {
   @IBInspectable var cornerRadius: CGFloat = 0.0 {
      didSet { self.layer.cornerRadius = cornerRadius }

Here, we are defining a variable called cornerRadius, and linking it with the layer’s corner radius. As a result, every time we set the cornerRadius attribute in the Attribute Inspector of IB, we will see the view change it’s corner radius attribute.

The most useful use case for IBInspectable, and maybe the first that comes to mind, is being able to change the cornerRadius, borderWidth and borderColor of a UIView, something that most probably every iOS developer has to deal with during the development of an App.

Use Case: PlaceholderTextView

One of the most annoying things about UITextView (for me, at least), was the omission of a placeholder like UITextFields have. In my experience, most of the times a UITextView is used in conjunction with other UITextFields, you have to implement a placeholder for giving them all the same appearance. Now with IBDesignable/IBInspectable, we can easily build a custom UITextView that includes a placeholder string and behaves exactly like a UITextField will when some text is entered. Let’s do it!

First, we’ll define our subclass of UITextView, and we’ll make it Designable. We’ll add some attributes for the placeholder string and color:

@IBDesignable class PlaceholderTextView: UITextView {
    // variables
    /** The string that will be put in the placeholder */
    @IBInspectable var placeholder: NSString? { didSet { setNeedsDisplay() } }
    /** color for the placeholder text. Default is UIColor.lightGrayColor() */
    @IBInspectable var placeholderColor: UIColor = UIColor.lightGrayColor()

XCode nasty bugs!The default values will be honored by the Attribute Inspector as the “Default” configuration for the attribute. If you plan to add some custom functionality, you would probably need to override the initializers init(frame: CGRect) and init(coder aDecoder: NSCoder). If you just override these two initializers, Xcode will complaint about AutoLayout crashing while trying to render your custom view onscreen. In order to avoid this, we will make use of the TARGET_INTERFACE_BUILDER condition to make sure that the initializers only get compiled for the App in runtime, not in IB.

For our PlaceholderTextView, we will have to make sure that if the user enters some text, the placeholder string disappears, and if the user removes all characters, the placeholder appears again. Thus, we will add ourselves as listeners of the UITextViewTextDidChangeNotification and UITextViewTextDidBeginEditingNotification notifications in a method called from our initialization methods:

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

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

The listerForTextChangedNotifications() method simply calls NSNotificationCenter.defaultCenter().addObserver(…).

func listenForTextChangedNotifications() {
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "textChangedForPlaceholderTextView:", name:UITextViewTextDidChangeNotification , object: self)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "textChangedForPlaceholderTextView:", name:UITextViewTextDidBeginEditingNotification , object: self)

We must assure that once the view disappears, we remove ourselves as observers, but how can we do that? In the (good?) old days before ARC, we could use the method dealloc(), but nowadays we cannot use this. As we don’t have a UIViewController here, we cannot resort to viewDidDisappear(…) or viewWillDisappear(…). What’s the solution? The UIView method willMoveToWindow(newWindow: UIWindow?) gets called whenever the UIView is about to move to a new window. This method gets called when the view disappears, with newWindow set to nil. Thus, we can easily check for this condition to remove ourselves as observers in NSNotificationCenter:

override func willMoveToWindow(newWindow: UIWindow?) {
    if newWindow == nil { NSNotificationCenter.defaultCenter().removeObserver(self, name: UITextViewTextDidChangeNotification, object: self) }
    else { listenForTextChangedNotifications() }

The method textChangedForPlaceholderTextView(…) will just refresh the view by calling setNeedsDisplay().

Also, there are other situations in which we would need to call setNeedsDisplay, concretely when any of the attributes relative to the text and how it’s rendered change, so we will override the following variables: text, attributedText, contentInset, font and textAlignment, and call setNeedsDisplay() thanks to the swift didSet mechanism.

Pick up your pencils

Now that we have defined when we have to refresh our subview, let’s actually define what will be drawn. We need to calculate the bounds where we will draw the placeholder text. This bounds can be easily calculated by referring to the container bounds, while taking into account the possible indentation due to the paragraph style:

func placeholderBoundsContainedIn(containerBounds: CGRect) -> CGRect {
    // get the base rect with content insets.
    let baseRect = UIEdgeInsetsInsetRect(containerBounds, UIEdgeInsetsMake(kPlaceholderTextViewInsetSpan, kPlaceholderTextViewInsetSpan/2.0, 0, 0))
    // adjust typing and selection attributes
    if typingAttributes != nil {
        if let paragraphStyle = typingAttributes[NSParagraphStyleAttributeName] as? NSParagraphStyle {
            baseRect.rectByOffsetting(dx: paragraphStyle.headIndent, dy: paragraphStyle.firstLineHeadIndent)
    return baseRect

Now we can override the drawRect method, that gets called whenever the view needs to be drawn/refreshed. The text will be drawn by calling string.drawInRect, being careful about the possible permutations in the paragraph style:

override func drawRect(rect: CGRect) {
    // in case we don't have a text, put the placeholder (if any)
    if countElements(self.text) == 0 && self.placeholder != nil {
        let baseRect = placeholderBoundsContainedIn(self.bounds)
        let font = self.font ?? self.typingAttributes[NSFontAttributeName] as? UIFont ?? UIFont.systemFontOfSize(UIFont.systemFontSize())
        // build the custom paragraph style for our placeholder text
        var customParagraphStyle: NSMutableParagraphStyle!
        if let defaultParagraphStyle =  typingAttributes[NSParagraphStyleAttributeName] as? NSParagraphStyle {
            customParagraphStyle = defaultParagraphStyle.mutableCopy() as NSMutableParagraphStyle
        } else { customParagraphStyle = NSMutableParagraphStyle.defaultParagraphStyle().mutableCopy() as NSMutableParagraphStyle }
        // set attributes
        customParagraphStyle.lineBreakMode = NSLineBreakMode.ByTruncatingTail
        customParagraphStyle.alignment = self.textAlignment
        let attributes = [NSFontAttributeName: font, NSParagraphStyleAttributeName: customParagraphStyle.copy() as NSParagraphStyle, NSForegroundColorAttributeName: self.placeholderColor]
        // draw in rect.
        self.placeholder?.drawInRect(baseRect, withAttributes: attributes)

I’m preparing a tutorial on advanced graphic drawing in Cocoa, but If you want to know more, there is a very nice book called Learning Cocoa with Objective-C, that I can recommend.

Viewing the results

Now that we have all the pieces in place, XCode will compile our subclass and, thanks to the @IBDesignable attribute, render the view using the drawRect. Thus, we will be able to change the placeholder text, its color, and observe the results in realtime in the Interface Builder, isn’t it cool?


You can find the full code for the PlaceholderTextView in my github.