Pushing the Limits of User-Defined Runtime Attributes in Interface Builder
来源:互联网 发布:linux 如何执行文件 编辑:程序博客网 时间:2024/05/16 13:45
User-defined runtimes attributes in Xcode’s Interface Builder are a great way to keep view controller and view code clean, while obeying Separation of Concerns.They allow you to configure properties on the view or view controller that you are unable to configure from within Interface Builder’s Attributes Inspector or Size Inspector.
But sometimes you may want to setup from Interface Builder a scenario which you can’t do from simple KVC-compliant property manipulation.
One example I ran into recently was trying to set the content insets on a collection view in a normal view controller in an iPhone-specific storyboard. In Xcode 5.1 you are unable to set the content insets of a collection view (I’m not sure in which Xcode version this was first missing – I’m sure you used to be able to do it).So with this feature missinh, it would be nice to be able to do the same thing using user-defined runtime attributes.
Because contentInset is of UIEdgeInsets
type, this isn’t possible with any of the types defined in Interface Builder under user-defined runtime attributes. But it can be done!
Another example I will show below is how to set the borderColor property of a CALayer
, even though the runtime attributes don’t support CGColorRef
type. All will be explained!
Runtime Attributes Explained
First a bit of background: user-defined runtime attributes can be set from theIdentity Inspector tab in Interface Builder utilities, as shown below:
They are defined as a set of key-value pairs. The key is actually a key path, which is a powerful, especially in Interface Builder where you don’t have direct access to the underlyingCALayer
of a UIView
(more on this below).
The limitation is that the object’s class must be KVC-compliant for the key (or key path) defined in the set of runtime attributes. To beKVC-compliant for a key foo
simply means instances of the class must respond to the selectorsetFoo:
.
Really, being KVC-compliant for a property means the class should implement the methods required for
valueForKey:
andsetValue:forKey:
to work for that property. For user-defined runtime attributes, that means implementing the setter for the property (key).
The following property types available for runtime attributes in Interface Builder:
- Boolean – translates to a
BOOL
property - Number – can translate to any numeric scalar property or a property of type
NSNumber *
- String – translates to a property of type
NSString *
- Localized String – the value here is a key to look up in the
strings
file for the current locale. - Point – translates to a
CGPoint
property - Size – translates to a
CGSize
property - Rect – translates to a
CGRect
property - Range – translates to an
NSRange
property - Color – translates to a property of type
UIColor *
- Nil – this spectial type doesn’t allow you to set a value, it is just a way of specifying that the value should be set to
nil
The types Point and Size can be used interchangeably, because they both map to the same type of C structure (
struct {CGFloat v1; CGFloat v2;}
).
Some Examples
Configuring a CALayer
A great example of using runtime attributes is to configure a UIView
’s underlyingCALayer
. For example, we can set the layer’s border width and corner radius as follows:
Unfortunately, Interface Builder doesn’t allow us to set the color of a CALayer, because theColor type doesn’t translate to properties of CGColorRef *
type (workaround discussed below).
Configuring Custom Controls
Another example is if you are using a custom UIControl
object such as a range slider. A range slider is similar to the built-in slider, but has two thumbs, or knobs: one to specify the minimum value and one to specify the maximum value.This kind of control would be useful for setting a minimum price and maximum price in a search query. There arevarious implementations available in the community.
Using user-defined runtime attributes, you can configure such a control right from within Interface Builder. Taking the example ofNMRangeSlider, you could configure the minimum and maximum values for the slider as follows:
The main benefit of Interface Builder in my opinion is that it keeps all the UI configuration logic in one place outside of the view controller. By configuring your controls in Interface Builder, this is yet more code that can be removed from your view controller. After all, you would configure your UIButton
and UISlider
controls in Interface Builder, so why not configure your custom controls too?
The Limitations
As discussed above, the class must be KVC-compliant for each key you specify in the runtime attributes. If you specify a key path, then the object returned for each key must be KVC-compliant for the following key in the path.
For example, the list of types shown above means that we can’t set the content inset on aUICollectionView
because there is no UIEdgeInset
type (and no type that uses the exact same C structure type).
We also already discussed the fact that we can’t set the border color of a CALayer
. Wouldn’t it be nice if anything you can think of, you could configure in Interface Builder without adding logic to your view or view controller?
The Workaround
Define a category on the object you wish to configure. Comply to KVC in the category and put the configuration logic in the setter method. You don’t even need to#import
the category header anywhere – just by having it in the project, the runtime will call the setter method.
Define a Category
For example, by defining a category on UICollectionView
, we can make the class KVC-compliant for any key we choose. To set the content inset, we can define the following category implementation:
12345678
@implementation UICollectionView (ContentInset)- (void)setContentInsetFromString:(NSString *)string{ self.contentInset = UIEdgeInsetsFromString(string);}@end
There’s no need to define anything in the category header.
This particular example is especailly simple because of the UIEdgeInsetsFromString
mathod, which could it seems have been created specifically for this purpose!
Configure the Interface
Now we can set the user-defined runtime attributes in Interface Builder like so:
The Key Path must match the name of the setter method in the category, but without theset prefix and with lowercase first letter.
Now you’ve configured your interface in Interface Builder – the correct place – and you can avoid having clutter in your code!
An added bonus of doing things in Interface Builder is that if you use separate nibs or storyboards for iPad/iPhone then you can configure your view differently for each device without cluttering your code checking the user interface idiom.
Another Example
Now we can define the border color of a CALayer
by simply mapping it from aUIColor
.
Define a Category
12345678
@implementation CALayer (Additions)- (void)setBorderColorFromUIColor:(UIColor *)color{ self.borderColor = color.CGColor;}@end
Configure the Interface
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 be left with a seemingly random border to their view, unable to figure out why it’s there.
So like anything of this nature — and by that I mean a more-advanced use of a programming language or development evironment — 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.
I hope you enjoyed reading this article. If so, please follow me on twitter for more of the same.
- Pushing the Limits of User-Defined Runtime Attributes in Interface Builder
- User Defined Runtime Attributes
- User Defined Runtime Attributes
- User Defined Runtime Attributes
- User Defined Runtime Attributes
- User Defined Runtime Attributes
- User Defined Runtime Attributes
- Expanding User-Defined Runtime Attributes in Xcode with Objective-C
- iOS User Defined Runtime Attributes to define the key path
- 2014资料整理--User Defined Runtime Attributes
- 搜索看看? 搜索 User Defined Runtime Attributes
- xib中User Defined Runtime Attributes使用
- Pushing the Limits of Windows: Paged and Nonpaged Pool
- Pushing the Limits of Windows: Paged and Nonpaged Pool
- Pushing the Limits of Deep CNNs for Pedestrian Detection
- StoryBoard之User Defined Runtime Attributes的使用
- <Android Programming Pushing the Limits> 读书笔记
- READING NOTE: Pushing the Limits of Deep CNNs for Pedestrian Detection
- fileupload上传,支持多文件上传
- Linux主机公钥/私钥配置
- DataContext ConnectionString
- HDOJ-2586(Tarjan算法)
- Unity3d NGUI的使用(十三)(UITextList制作聊天窗口)
- Pushing the Limits of User-Defined Runtime Attributes in Interface Builder
- 2014中国软件开发者调查(三):移动应用、游戏开发技术应用特点【转】
- 关于UIImage自动释放问题
- Unity3d NGUI的使用(十四)(NGUI制作Tab形式UI)
- [Vertica报错]Spread error; can't determine which DBs are running; attempting to continue... '
- CloudFoundry Admin-UI 配置使用
- servletinputstream 和 servletoutputstream
- MC/DC逻辑覆盖
- 升级那点事