More Interface Builder
When I started iPhone programming, I built most of my interfaces in code. It was much easier: full control over your interface. Sometimes I would start in Interface Builder, but once I stumbled upon limitations I would rewrite the whole thing in code.
After learning some more things about Interface Builder, I started using it more and more. These days, I try to do almost every UI in Interface Builder. Apple has made it a lot easier for us, too: designing your table view cells in Interface Builder was quite easy already, but is even easier with Storyboards.
Here’s one example where I used to revert to code, but now use nibs. One of the projects I worked on required a horizontally paging scrollview. Each page was for answering a question, and contained a description and a slider. If you want to play with the example, please download the project from github. Let’s see how to do this using mostly IB:
Start by creating a view controller and adding a scroll view to it. Create an
outlet scrollView on the view controller. Then, create a new empty XIB. We add a
single UIView to the XIB with nested subviews (the text label and the slider).
Then, in the view controller, add a method to setup the pages and call it fromviewDidLoad
. Note that the method is a bit long, normally you would probably
factor it into components:
- (void)setupPages {
pages = [NSMutableArray arrayWithCapacity:NUM_PAGES];
CGFloat pageWidth = self.scrollView.bounds.size.width;
CGRect pageFrame = self.scrollView.bounds;
self.scrollView.contentSize = CGSizeMake(pageWidth*NUM_PAGES, self.scrollView.bounds.size.height);
for(int i = 0; i < NUM_PAGES; i++) {
NSArray* topLevelObjects = [[NSBundle mainBundle] loadNibNamed:pageNibName owner:nil options:nil];
UIView* pageView = [topLevelObjects lastObject];
pageFrame.origin.x = pageWidth * i;
pageView.frame = pageFrame;
[self.scrollView addSubview:pageView];
[pages addObject:pageView];
}
}
For this code to work, it’s important that there is only one view at the
top-level of the xib. The above code creates the pages and adds them to the
subviews. However, the separate pages don’t have a way to access the label and
slider. A crude way to do this is give both of them a tag, and use something
like [pageView viewWithTag:LABEL_TAG]
. Indeed, for small views this can work,
but it gets messy and hard to maintain quickly.
A better way is to add a new subclass of UIView, named CEPageView
and change
the root-element of PageView.xib to that UIView
subclass in the Identity
Inspector. If you open the Assistant Editor, you can create outlets on that
newly created class and connect them to the XIB. Note that normally, you would
drag from a view to the File’s Owner, now you drag to the root element in the
Top Level Objects. Change the body of the for-loop to this:
NSArray* topLevelObjects = [[NSBundle mainBundle] loadNibNamed:pageNibName owner:nil options:nil];
CEPageView* pageView = [topLevelObjects lastObject]; // change
pageFrame.origin.x = pageWidth * i;
pageView.frame = pageFrame;
[self.scrollView addSubview:pageView];
[pages addObject:pageView];
// Add these lines:
pageView.title.text = [NSString stringWithFormat:@"Page %d", i+1];
pageView.slider.value = i / 3.0;
You can also override the layoutSubviews of the UIView if you want some programmatic control over the layout.
I hope that this technique will make you more productive. I’m no expert on Interface Builder, so if you have any tips let me know!
Update
: Ole Begemann recommends using UINib instead of loadNibNamed:owner
,
it should be faster when loading a nib multiple times. I haven’t tested it yet,
but will check it out. See also: NSBundle vs. UINib
performance