In Xcode 6 two new interface builder declaration attributes were introduced: IBInspectable
and IBDesignable
. IBInspectable
exposes 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:
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:
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.
Select Framework & Application Library
and choose the Cocoa 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.
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: UIColor
, borderWidth: CGFloat
, cornerRadius: 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.
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.
After playing a bit with the cornerRadius
I found out that it creates some interesting patterns when you add big values.
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
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.
UIImage is also IBInspectable
Thanks
I’ve added it to the list.
I bet you learned this from one of the WWDC 2014 videos.
I didn’t wait for the WWDC session videos. I just read the book and the documentation
Hey very nice website!! Man .. Beautiful .. Amazing ..
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?
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
Any idea why your class needs to be in a framework for this to work?
I mentioned in the article that this might be the way that xcode loads your component.
Is this still the case? Or was that a beta thing?
nope… now you can make any UIVIew @IBDesignable
can only UIViews be @IBDesignable? Can I make a UITableViewCell @IBDesignable?
All subclasses of UIView can be @IBDesignable – including UITableViewCell
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!
That’s a good question. I’ve tried using IBDesignable (without the @) in Objective-C but that didn’t work. There should be a method of doing this, when I’ll find out I will post it here.
I believe in objective-c it requires adding #IB_DESIGNABLE
The correct equivalent in Objective-C are `IB_DESIGNABLE` and `IBInspectable`.
#import
IB_DESIGNABLE
@interface MyDrawFrameRectViewClass : UIView
@end
IB_DESIGNABLE
Reference:
http://stackoverflow.com/questions/24465679/xcode6-ibdesignable-and-ibinspectable-with-objective-c
Apple Documentation:
https://developer.apple.com/library/ios/recipes/xcode_help-IB_objects_media/chapters/CreatingaLiveViewofaCustomObject.html#//apple_ref/doc/uid/TP40014224-CH41-SW1
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
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.
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
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.
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
How could this be rewritten in Swift? (trying to play a video from disk)
NSURL *videoStreamURL = [[NSBundle mainBundle] URLForResource:@”IMG_0735″ withExtension:@”mov”];
NSBundle.mainBundle().URLForResource(“IMG_0735”, withExtension: “mov”)
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.
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.
Thank you for an excellent tutorial! It seems that Xcode 6 finally supports visual design-time editing without a separate framework creation!
Thanks for that, it helps a lot.
Is there a way to make UIFont Inspectable ? It would helps for Labels…
Really awesome – thanks Andrei!
Really great article and video. Thanks!
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
Hey nice blog. Really helpful.