I posted a while back about the UINib class which was a minor but useful addition to iOS 4.0. Today I want to mention another iOS 4.0 addition that sneaked in under the radar - Interface Builder Outlet Collections.
Interface Builder Outlets
Interface Builder Outlets are nothing new. The IBOutlet
identifier added to a variable declaration in your class header file provides a hint to Interface Builder. The outlet representing an instance variable in your class can then be visually connected to the UI object in Interface Builder.
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet NibCollectionViewController *viewController;
Interface Builder Outlet Collections
IB Outlet Collections extend the concept of an IB Outlet to allow one outlet to be associated with a collection of IB objects. To illustrate why this might be useful consider the following example of a user interface containing a number of switches and a reset button (I don’t expect to win any prizes for UI design here but stick with me):
The UISwitch
elements can all be accessed individually but when the reset button is used the switches are all reset to the off position. Using just IBOutlets
you could create an instance variable for each switch and connect each one up in turn. The action that is invoked when the reset button is used would then access each switch in turn and set the switch to off.
IB Outlet Collections provide an easier solution. Instead of defining instance variables for each UISwitch
we use an NSArray
to represent the collection of switches:
@interface NibCollectionViewController : UIViewController {
NSArray *switchCollection;
}
@property (nonatomic, retain) IBOutletCollection(UISwitch) NSArray *switchCollection;
- (IBAction)resetAction:(id)sender;
- (IBAction)switchAction:(id)sender;
@end
Instead of using the IBOutlet
macro we use IBOutletCollection
which takes an optional parameter to specify the class name of the objects in the collection. When specified IB requires all objects to be of that class. So in this case we can specify UISwitch
since the collection only contains switches. We will look at a second example later when the objects in the collection are not all of the same type.
With the macro defined we can the connect up the outlets in Interface Builder. Looking at the connections for one of the UISwitch
items, instead of making the connection from “Referencing Outlets” it is made from “Referencing Outlet Collections”.
A connection is made from each switch to the Outlet Collection so that it ends up as follows:
The method resetAction
is connected to reset button and looks as follows:
- (IBAction)resetAction:(id)sender {
NSNumber *no = [NSNumber numberWithBool:NO];
[switchCollection setValue:no forKey:@"on"];
}
The method resetAction
makes use of Key Value Coding (KVC) to set a value for the “on” property for each item in the switchCollection
array. Note that since the “on” property of a UISwitch
is actually a BOOL
we need to wrap the “NO” value in an NSNumber
. The implementation of setValue:forKey
takes care of extracting the BOOL
value from the NSNumber
object.
This method works well but it misses one small refinement by setting the “on” property directly we lose the ability to animate the motion of the switches as they reset. To animate the reset action we need to send the message setOn:animated
to each of the switches in the array. So an alternative implementation for resetAction
with animation could be as follows:
- (IBAction)resetAction:(id)sender {
for (UISwitch *swItem in switchCollection) {
[swItem setOn:NO animated:YES];
}
}
Collections of Different Objects
The example so far works with a single object type UISwitch
in the collection but the use of Outlet Collections is not limited to a single object type. As I mentioned previously the class name parameter is optional for the IBOutletCollection
. If you omit it (or specify id) you can include any type of UI element you want. So for example, consider a situation where I want to be able to enable or disable a mix of UI elements in one operation.
The property statement now becomes as follows:
@property (nonatomic, retain) IBOutletCollection(id) NSArray *elementCollection;
Since all of the UI elements in the collection have a property named “enabled” we can again make use of Key Value Coding to disable all of the elements:
NSNumber *no = [NSNumber numberWithBool:NO];
[elementCollection setValue:no forKey:@"enabled"];
Wrapping Up
As with the UINib
class the use of IBOutletCollections
can be considered a minor addition to the ever expanding set of iOS frameworks. Like UINib
I think it is well worthwhile having it in your toolbox for those occasions when you need to perform the same operation on multiple UI elements.