[Objc] User defined runtime attributes

User defined attributes are the hidden gem of Xcode’s Interface Builder. Available since Xcode 4 and 5, they provide the ability to configure 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.

 

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 –
Size – CGSize
Rect –
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.cornerRadiuskey paths, as shown below.

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.

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:

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.

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.

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;
}

[…]

Read article herehttp://ios-blog.co.uk/tutorials/user-defined-runtime-attributes


warningImportant note:
this article is automatically grabbed from here. I’m not the owner and the responsible of this post content.