How to make awesome UI components in iOS 8 using Swift and XCode 6

In Xcode 6 two new interface builder declaration attributes were introduced: IBInspectable and IBDesignableIBInspectableexposes class properties in the interface builder Attribute Inspector, and IBDesignable updates the view in realtime! They are pure magic!

I can’t wait to see what cool stuff you will make.

tl;dr

a short tutorial on how to use IBInspectable and IBDesignable with a video demo. It should take you around 10 minutes to go through all the steps. code on github

IBInspectable

These are the valid types for IBInspectable I found so far:

  • Int
  • CGFloat
  • Double
  • String
  • Bool
  • CGPoint
  • CGSize
  • CGRect
  • UIColor
  • UIImage

Example:

 

class OverCustomizableView : UIView {
    @IBInspectable var integer: Int = 0
    @IBInspectable var float: CGFloat = 0
    @IBInspectable var double: Double = 0
    @IBInspectable var point: CGPoint = CGPointZero
    @IBInspectable var size: CGSize = CGSizeZero
    @IBInspectable var customFrame: CGRect = CGRectZero
    @IBInspectable var color: UIColor = UIColor.clearColor()
    @IBInspectable var string: String = "We ❤ Swift"
    @IBInspectable var bool: Bool = false
}

In the Attribute Inspector you will see this on top:

exposed properties

All this does is add some user defined runtime attributes that will set the initial values of the view when it loads.

The runtime attributes created:

user define runtime attributes in xcode 6

IBDesignable

Now for the fun part. IBDesignable tells Interface Builder that it can load the view and render it. The view class must be in a framework for this to work. But that is not a big inconvenience, as we will soon see. I think that under the hood Interface Builder converts your UIView code into NSView code so that it could dynamically load the framework and render the component.

Create a new project

Open Xcode 6, create a new “Single Page Application” and select Swift as the programming language.

Add a new target to you project

Select your project file from the navigator and add a new target by pressing the + button.

add a new target in xcode

Select Framework & Application Library and choose the Cocoa Touch Framework.

creating a cococa touch framework

Name it MyCustomView. Xcode will automatically link the MyCustomView.framework to your project.

Create a custom view class

Create a new swift file and add it in MyCustomView framework.

Right click on the framework’s directory.

create new file

Select Cocoa Touch File

Name it CustomView and make it a subclass of UIView

CustomView.swift should contain:

import UIKit

class CustomView: UIView {

    init(frame: CGRect) {
        super.init(frame: frame)
        // Initialization code
    }

    /*
    // Only override drawRect: if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func drawRect(rect: CGRect)
    {
        // Drawing code
    }
    */

}

Remove the generated methods.

import UIKit

class CustomView: UIView {

}

Tell Xcode to render your view with the @IBDesignable keyword.

Add three properties: borderColor: UIColorborderWidth: CGFloatcornerRadius: CGFloat.

Set the default values and make them inspectable.

@IBDesignable class CustomView : UIView {
    @IBInspectable var borderColor: UIColor = UIColor.clearColor() 
    @IBInspectable var borderWidth: CGFloat = 0 
    @IBInspectable var cornerRadius: CGFloat = 0 
}

Add logic for the layer properties

Add property observers for each property and update the layer accordingly.

class CustomView : UIView {
    @IBInspectable var borderColor: UIColor = UIColor.clearColor() {
        didSet {
            layer.borderColor = borderColor.CGColor
        }
    }

    @IBInspectable var borderWidth: CGFloat = 0 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }

    @IBInspectable var cornerRadius: CGFloat = 0 {
        didSet {
            layer.cornerRadius = cornerRadius
        }
    }
}

Build the framework by pressing ⌘ Cmd + B.

Test the custom view

Open Main.storyboard and add a view from the component library.

Change the view class to CustomView using the Identity Inspector.

chande the class

Arrange the view and add any autolayout constraints that are needed.

Tip: Hold Crtl + ⌘ Cmd then click and drag the mouse cursor from a view to another to add autolayout constraints.

adding autolayout contraints without headaches

After playing a bit with the cornerRadius I found out that it creates some interesting patterns when you add big values.

interesting pattern

 

You can get the code on github.

Have fun :)

A quick demo:

Challenges

  • make a checkbox component
  • make a component that has an angle: CGFloat property that rotates the view
  • render a fractal in Interface Builder :)

If you found this useful please take a moment to share this with your friends :)

  37 comments for “How to make awesome UI components in iOS 8 using Swift and XCode 6

  1. Christopher Penrose
    June 6, 2014 at 7:41 pm

    This is a fantastic addition. I sincerely hope that the introspection machinery that is used to provide such magic is made directly available in the Swift language itself.

  2. Andrew
    June 9, 2014 at 1:40 am

    UIImage is also IBInspectable

    • June 9, 2014 at 7:29 am

      Thanks :) I’ve added it to the list.

      • Dude
        June 9, 2014 at 9:20 am

        I bet you learned this from one of the WWDC 2014 videos. :P

        • June 9, 2014 at 9:26 am

          I didn’t wait for the WWDC session videos. I just read the book and the documentation :)

  3. Johne447
    June 9, 2014 at 7:56 pm

    Hey very nice website!! Man .. Beautiful .. Amazing ..

  4. June 13, 2014 at 9:27 pm

    Are there any downsides that the view needs to be in a framework or can I just create a framework: my designable views and add all views I like to it?

    • June 14, 2014 at 12:15 am

      Not really, more than that it brings benefits. Think how many components you are going to use in the future. Apple provides ~30. There will be thousands on github by the end of the year ;)

  5. Adam
    June 13, 2014 at 10:00 pm

    Any idea why your class needs to be in a framework for this to work?

    • June 14, 2014 at 12:14 am

      I mentioned in the article that this might be the way that xcode loads your component.

      • Chris
        December 3, 2014 at 11:11 pm

        Is this still the case? Or was that a beta thing?

        • December 4, 2014 at 12:21 am

          nope… now you can make any UIVIew @IBDesignable

          • Kyle
            June 3, 2015 at 3:05 pm

            can only UIViews be @IBDesignable? Can I make a UITableViewCell @IBDesignable?

          • June 3, 2015 at 3:07 pm

            All subclasses of UIView can be @IBDesignable – including UITableViewCell

  6. Will
    June 14, 2014 at 2:18 am

    I know this is a swift concentrated website, but has it been discovered how to have an IBIspectable’s value changed in IB to be redrawn with Objective-C?

    I know that in Swift you use the didSet method, but I have not been able to get IB to work with any Objective-C equivalent methods to be able to change the value in IB and also see the change redrawn in IB.

    Thanks in advance!

  7. stu
    June 14, 2014 at 5:55 am

    Got lost at “Write a class definition above the view controller class.” until”Add logic to the layer properties”.

    Where does that code go?

    Cheers

    • June 14, 2014 at 11:27 am

      There was a mistake in the tutorial. Thank you for spotting it :)
      I’ve change the ‘Create a custom view class’ step. The CustomView class should be in the framework.

  8. Doug Smith
    June 16, 2014 at 12:31 am

    I followed this to a T, but when I set the corner radius in Interface Builder nothing happens. What am I doing wrong? Here’s the project, it’s just implementing this guide: http://cl.ly/0z0y2Y3L0K2u

  9. Ratha
    June 16, 2014 at 9:43 am

    Thank you for make this tutorial. I would like to know how we link customView to nib in Swift?

    In Objective-C we use:

    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@”KeyboardView” owner:self options:nil];
    [[nib objectAtIndex:0] setFrame:frame];
    self = [nib objectAtIndex:0];

    But if I do this in Swift:

    let nib = NSBundle.mainBundle().loadNibNamed(“KeyboardView”, owner: self, options: nil)
    let nibView:UIView = nib[0] as UIView
    nibView.frame = self.frame;

    The app will crash.

    • June 16, 2014 at 12:35 pm

      this should work

      // func instantiateWithOwner(ownerOrNil: AnyObject!, options optionsOrNil: NSDictionary!) -> AnyObject[]!

      var nib = UINib(nibName: "KeyboardView", bundle: nil)
      let nibObjects = nib.instantiateWithOwner(self, options: nil)
      let nibView = nibObjects[0] as UIView

  10. ArtS
    July 1, 2014 at 9:03 pm

    How could this be rewritten in Swift? (trying to play a video from disk)

    NSURL *videoStreamURL = [[NSBundle mainBundle] URLForResource:@”IMG_0735″ withExtension:@”mov”];

    • July 1, 2014 at 9:32 pm

      NSBundle.mainBundle().URLForResource(“IMG_0735”, withExtension: “mov”)

  11. Kevin Altis
    July 10, 2014 at 9:38 pm

    As of Xcode 6 beta 3 you no longer need to use a framework, at least with Swift. However, they appear to have broken some inspectable elements. On my test custom view the following fail to show up:

    @IBInspectable var integer: Int = 0
    @IBInspectable var double: Double = 0
    @IBInspectable var string: String = “We ❤ Swift”
    @IBInspectable var bool: Bool = false

    I was able to get integer to show up as a NSInteger and string to show up as NSString, but it doesn’t really work correctly. I have not posted a bug report yet since I don’t know if I’m simply doing it wrong.

    Note that if you are making changes to your inspectable variables and start seeing warnings or errors about being unable to set a value at runtime, be sure to check the list in the Identity Inspector. Even if you have changed the source code, the User Defined Runtime Attributes doesn’t always update correctly.

    The other problem I’ve had is getting an image from my Images.xcassets to display in the live view. I added a 1x and 2x image named “card” to the project and a drawRect method in the custom view:

    override func drawRect(rect: CGRect) {
    let card = UIImage(named:”card”)
    card.drawInRect(rect)
    }

    I’m not checking for card == nil because I wanted to see if some kind of error was occurring, but there is no warning or crash, just silent failure. I have another custom class that just draws with UIBezierPath and it works correctly.

    The custom view that is supposed to display an image works correctly if you run the app, it just doesn’t display the image or give any error indication in live view. You can’t set a breakpoint or even get println output during the live view rendering that I can see, so this is frustrating to debug.

  12. Kevin Altis
    July 10, 2014 at 10:23 pm

    Also, do you have an example using

    prepareForInterfaceBuilder()

    or

    #if TARGET_INTERFACE_BUILDER

    Neither of these seem to be working in Xcode 6 beta 3 with Swift.

  13. Dmitry
    October 14, 2014 at 1:37 am

    Thank you for an excellent tutorial! It seems that Xcode 6 finally supports visual design-time editing without a separate framework creation!

  14. Alex
    October 14, 2014 at 2:51 pm

    Thanks for that, it helps a lot.
    Is there a way to make UIFont Inspectable ? It would helps for Labels…

  15. Malcolm
    October 19, 2014 at 10:20 am

    Really awesome – thanks Andrei!

  16. Pingback: Build a Swift App
  17. Stephaughn Alston
    March 24, 2015 at 1:19 pm

    Really great article and video. Thanks!

  18. LightRider
    May 27, 2015 at 8:31 am

    you explain it quite clearly and easily for me, thank you

    I don’t know if I express correctly, sorry for my poor english

    this tutorial is really good

Leave a Reply

Your email address will not be published. Required fields are marked *

Subscribe
We send about one email per week with our latest tutorials and updates
Never display this again :)