Interface Builder Outlet Collections

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.