Show Menu

Do you have a job opening?

Submit your 30 day Job Listing for FREE

User defined runtime attributes are the hidden gem of Xcode’s Interface Builder. Available since Xcode 4 and iOS 5, they provide the ability to configure properties of a view that you would otherwise be unable to configure in Interface Builder.

As an advocate for the separation of concerns, I believe we should do in Interface Builder as much interface configuration as possible. Although runtime attributes are often overlooked, they can lead to much cleaner view controller code – something we can all benefit from.

Usage

User defined runtime attributes are set from the Identity Inspector tab in Interface Builder’s utilities, as shown below.

iOS: user defined runtime attributes

They are defined as a set of key path/value pairs where the view must be Key Value Coding-compliant for each of the given key paths.

Attribute Types

Below is a list of the available attribute types and the corresponding property type.

  • Boolean – BOOL
  • Number – NSNumber * or any numeric scalar, e.g. NSInteger
  • String – NSString *
  • Point – CGPoint
  • Size – CGSize
  • Rect – CGRect
  • Range – NSRange
  • Color – UIColor *

There are also the following special types:

  • Nil – this special type doesn’t allow you to set a value, it is just a way of specifying that the property should be set to nil
  • Localized String – the value here is a key to look up in the strings file for the current locale, the property is set to the corresponding localized string

Simple Examples

Configuring a view’s underlying CALayer is not possible in Interface Builder’s Attributes Inspector. But with runtime attributes we can easily give the view a border and rounded corners by setting the layer.borderWidth and layer.cornerRadius key paths, as shown below.

iOS calayer setup

Configuring a custom control has never been easier! For example, if you create your own range slider (like a UISlider but with two thumbs to specify a minimum and maximum value in a range), and add it to a view in Interface Builder, then you can configure it as follows to avoid cluttering the view controller with unnecessary view configuration code.

custom-control-setup

Converting From Other Types

Any attribute type can be used to configure a property of another type as long as the type is represented in the same way. For example Point and Size can be used interchangeably, but they can also be used to represent a property of type CGVector, because the underlying structure is the same (i.e. two floating point numbers).

But you can also use another little trick to configure a property of a type otherwise unsupported by Interface Builder. For example, continuing on from configuring a view’s underlying CALayer, you may wish to set the border colour or shadow colour. This isn’t directly supported using runtime attributes, because these properties are of type CGColorRef. But consider the following example:

iOS calayer border color

In order to make CALayer KVC-compliant for the property borderColorFromUIColor, simply implement the setter for the property. This can be done in a category on CALayer as follows:


@implementation CALayer (Additions)

- (void)setBorderColorFromUIColor:(UIColor *)color
{
  self.borderColor = color.CGColor;
}

@end

Convert From String

It is often useful to convert from a string to another type. For example, configuring a property of type UIEdgeInsets isn’t directly supported by runtime attributes, but consider the following category:

@implementation UIScrollView (Additions)

- (void)setContentInsetFromString:(NSString *)contentInsetString
{
  self.contentInset = UIEdgeInsetsFromString(contentInsetString);
}

@end

It’s particularly easy to convert from a string to UIEdgeInsets type, because of the nice UIEdgeInsetsFromString method provided by UIKit. But you could define your own method to convert from a string to any other type you can think of. Get creative!

More Advanced Uses

Often it is useful to be able to set some attributes of a view where the attribute doesn’t have a corresponding property on the view.

Imagine a scenario where you have various UIView views within a view controller. You want to use UIKit Dynamics to make each view drop to the bottom of the screen and bounce. And you want to define how high it should bounce in Interface Builder, because after all, you are building the interface!

Consider the following setup for runtime attributes on a UIView.

iOS elasticity

UIView is not KVC-compliant for the key path elasticity. And an exception will be raised if you run the app now. But you can change this by defining a category on UIView with the property elasticity (see category properties).

Now you can use this property when you configure the UIDynamicItemBehavior in your view controller. This is nice because it means you don’t have to have logic in your view controller for each view individually. Your view controller only needs to know about a collection of views (i.e. IBOutletCollection). The specific configuration of each is done in Interface Builder, where it belongs.


for (UILabel *label in self.labels) {
  UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[label]];
  [self.animator addBehavior:itemBehavior];
  itemBehavior.elasticity = label.elasticity;
}

Considerations

I think this is the right way to configure a user interface. Given that we have Interface Builder, we should use it to its full potential. But… there’s always a “but” isn’t there?

Many people wouldn’t think to look in Interface Builder at the user defined runtime attributes, so they could be left very puzzled if they come to maintain your code at a later date. They could spend hours trying to figure out why a view has a border, and resort to overriding it in the view controller just to remove it.

So like anything of this nature — and by that I mean a more-advanced use of a programming language or development environment — we should use it with caution. Use it only if it is necessary and simplifies logic elsewhere in your code.

Setting up a view in a view controller isn’t necessarily messy. The most valid reason for using user-defined runtime attributes is to configure a view differently whether the Interface Builder file is for iPhone or iPad. Because you already have a separate nib or storyboard file specifically for each device, that should be the place that you do any device-specific configuration.

There are plenty of other uses for runtime attributes. I’d be keen to hear any creative examples you have thought of and I’d be happy to discuss the pros and cons at greater length, so please comment!

having issues?

We have a Questions and Answer section where you can ask your iOS Development questions to thousands of iOS Developers.

Ask Question

FREE Download!

Get your FREE Swift 2 Cheat Sheet and quick reference guide PDF download when you sign up to SwiftMonthly


Sharing is caring

If you enjoyed this tutorial, please help us and others by sharing using one of the social media buttons below.


Written by:

Sam is an iOS developer at The App Business in London. Keen on creating clean architectures and simplifying complex logic, he gets his real kicks from pushing the limits of Apple's frameworks and getting lost in the Objective-C runtime.

Comments

comments