IBOutletCollection set ordering in Interface Builder IBOutletCollection set ordering in Interface Builder ios ios

IBOutletCollection set ordering in Interface Builder


EDIT: Several commenters have claimed that more recent versions of Xcode return IBOutletCollections in the order the connections are made. Others have claimed that this approach didn't work for them in storyboards. I haven't tested this myself, but if you're willing to rely on undocumented behavior, then you may find that the explicit sorting I've proposed below is no longer necessary.


Unfortunately there doesn't seem to be any way to control the order of an IBOutletCollection in IB, so you'll need to sort the array after it's been loaded based on some property of the views. You could sort the views based on their tag property, but manually setting tags in IB can be rather tedious.

Fortunately we tend to lay out our views in the order we want to access them, so it's often sufficient to sort the array based on x or y position like this:

- (void)viewDidLoad{    [super viewDidLoad];    // Order the labels based on their y position    self.labelsArray = [self.labelsArray sortedArrayUsingComparator:^NSComparisonResult(UILabel *label1, UILabel *label2) {        CGFloat label1Top = CGRectGetMinY(label1.frame);        CGFloat label2Top = CGRectGetMinY(label2.frame);        return [@(label1Top) compare:@(label2Top)];    }];}


I ran with cduhn's answer and made this NSArray category.If now xcode really preserves the design-time order this code is not really needed, but if you find yourself having to create/recreate large collections in IB and don't want to worry about messing up this could help (at run time). Also a note: most likely the order in which the objects were added to the collection had something to do with the "Object ID" you find in the Identity Inspector tab, which can get sporadic as you edit the interface and introduce new objects to the collection at a later time.

.h

@interface NSArray (sortBy)- (NSArray*) sortByObjectTag;- (NSArray*) sortByUIViewOriginX;- (NSArray*) sortByUIViewOriginY;@end

.m

@implementation NSArray (sortBy)- (NSArray*) sortByObjectTag{    return [self sortedArrayUsingComparator:^NSComparisonResult(id objA, id objB){        return(            ([objA tag] < [objB tag]) ? NSOrderedAscending  :            ([objA tag] > [objB tag]) ? NSOrderedDescending :            NSOrderedSame);    }];}- (NSArray*) sortByUIViewOriginX{    return [self sortedArrayUsingComparator:^NSComparisonResult(id objA, id objB){        return(            ([objA frame].origin.x < [objB frame].origin.x) ? NSOrderedAscending  :            ([objA frame].origin.x > [objB frame].origin.x) ? NSOrderedDescending :            NSOrderedSame);    }];}- (NSArray*) sortByUIViewOriginY{    return [self sortedArrayUsingComparator:^NSComparisonResult(id objA, id objB){        return(            ([objA frame].origin.y < [objB frame].origin.y) ? NSOrderedAscending  :            ([objA frame].origin.y > [objB frame].origin.y) ? NSOrderedDescending :            NSOrderedSame);    }];}@end

Then include the header file as you chose to name it and the code can be:

- (void)viewDidLoad{    [super viewDidLoad];    // Order the labels based on their y position    self.labelsArray = [self.labelsArray sortByUIViewOriginY];}


Not sure when this changed exactly, but as of Xcode 4.2 at least, this no longer seems to be a problem. IBOutletCollections now preserve the order in which the views were added in Interface Builder.

UPDATE:

I made a test project to verify that this is the case: IBOutletCollectionTest