Cocoa has long defined the IBAction and IBOutlet keywords. These keywords provide metadata hints to Interface Builder providing it some understanding of your underlying code so you can graphically wire instance variables and properties and set target-action behaviors. For instance, whenever you need a pointer to NIB-defined object you would create a property defined as:
1
2
3
4
5
|
@
interface
MainViewController
:
UIViewController
{
UIButton *
saveButton
;
}
@
property
(
nonatomic
,
retain
)
IBOutlet
UIButton *
saveButton
;
@
end
|
The inclusion of the IBOutlet
keyword in the saveButton’s property definition makes it visible to Interface Builder allowing this connection to be wired graphically as show below:
Using IBOutlet makes it very easy to define your UI in Interface Builder and simply “wire” the object references to your code. However, it does come with one major drawback in that the relationship from a NIB-defined object to property/ivar is always 1-to-1; there was no way to define a collection of components as a single property in your code. Thankfully, Apple quietly introduced a new keyword in iOS4+ called IBOutletCollection allowing you to do just this. Let’s take a closer look at an example where this can be useful.
IBOutletCollection
Like the other keywords previously mentioned, you’ll find the IBOutletCollection keyword in UINibDeclarations.h, which is part of the UIKit framework. The new keyword is defined as follows:
#ifndef IBOutletCollection |
You’ll notice that IBOutletCollection
takes a class name parameter. This allows you specify what are considered valid values for the collection such as UIView
or UIButton
. If you’d prefer you can change the “type” argument to id
to allow for a heterogeneous collection. So how could this be useful?
Putting IBOutletCollection To Work
I’ve often found times where I needed references to certain components, but really only in an aggregate way so I could change state on them as a group. To do this I’d first need to define separate ivar/property combinations for each component, wire them up in Interface Builder, and then manually collect each pointer into an NSArray and store the collection for later use. Although this works, it is rather tedious and does add unnecessary bloat to your code. A better way to handle this type of scenario is to make use of the IBOutletCollection keyword and wire up this relationship in IB.
In the example below I’ve defined two unique ivars/properties to hold references to my on & off buttons and then an NSArray to hold a collection of other buttons. The on/off buttons are used to change the enabled
state of the group of “otherButtons”.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@
interface
MainViewController
:
UIViewController
{
UIButton *
onButton
;
UIButton *
offButton
;
NSArray *
otherButtons
;
}
@
property
(
nonatomic
,
retain
)
IBOutlet
UIButton *
onButton
;
@
property
(
nonatomic
,
retain
)
IBOutlet
UIButton *
offButton
;
@
property
(
nonatomic
,
retain
)
IBOutletCollection
(
UIButton
)
NSArray *
otherButtons
;
-
(
IBAction
)
toggleButtons
:
(
id
)
sender
;
@
end
|
@implementationMainViewController
for ( id button in self . otherButtons ) {
[ button setEnabled : state ] ;
}
self . onButton . enabled = ! state ;
self . offButton . enabled = state ;
}
[ super viewDidLoad ] ;
[ self setButtonsState : YES ] ;
}
[ self setButtonsState : sender == onButton ] ;
}
self . onButton = nil ;
self . offButton = nil ;
self . otherButtons = nil ;
[ super dealloc ] ;
}
I can now easily wire this together in Interface Builder without the need to write any code to collect and store the references to my group of buttons. This is clearly a contrived example, but I hope you can see the benefit of this small addition to iOS 4.