I don’t use Interface Builder (IB). Not anymore.

Somewhere between going through the iPhone to iPad display transition, supporting both portrait and landscape orientation and building screens that are dynamic and parts of it animated, I discovered that life without IB is much sweeter.

Why IB?

IB lets you lay out your user interface visually, with inspectors to tweak the controls’ properties. If you work with a designer who is not into programming, it is supposed to let you send the XIBs/NIBs to them and who can tweak it directly without requiring additional work from the developer.

If you have custom built components or do even a little bit of layout in code, your designer wouldn’t be able to see how the UI looks like in IB. Also, tweaks could mean animate this button for 0.1 seconds longer which requires changes in the code to see the result.

UI Reuse

You need to keep 2 set of NIBs, 1 for iPhone and 1 for iPad. Controls may need to be laid out differently in different screens, or you might have more functionality when running on the iPad due to the additional screen real estate available.

But often, there is UI that is re-usable across the two screen sizes. Creating your view programmatically makes it much easier to share them. For example, you can detect or pass in the actual screen size.

When you have your views written in code, you can decompose them into functions, methods, classes. Much better than in IB which pretty much can’t be reused.

Portrait and Landscape Orientation

Let’s face it: the layout system in IB is quite primitive. It lacks the layout managers provided in systems such as Java and Smalltalk. If you use IB, you often do up the portrait mode screen in NIBs and then run code to adjust them when changing to landscape orientation. This mixing of IB and code makes things more confusing and hard to architect.

Fine. So How Do We Do It in Code Instead?

For each screen, I have a controller class and view class pair. Here’s the controller class:

@interface MyViewController : UIViewController

@property (nonatomic,retain) UIView* mainView;

@end


@implementation MyViewController

@synthesize mainView;

- (void)loadView {
    self.view = [[[UIView alloc] initWithFrame:
        [UIScreen mainScreen].bounds] autorelease];
    self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth |
        UIViewAutoresizingFlexibleHeight;
    self.view.backgroundColor = [UIColor whiteColor];
}

- (void)viewDidLoad {
    [super viewDidLoad];

    self.mainView.autoresizingMask = UIViewAutoresizingFlexibleWidth |
        UIViewAutoresizingFlexibleHeight;
    //self.mainView.delegate = self;  //good to have
    [self.view addSubview:self.mainView];

    /* TODO: controller is displayed for the first time, do things like
        load data, etc */
}

- (void)viewDidUnload {
    [super viewDidUnload];

    self.mainView = nil;
}


- (void)dealloc {
    /* Call -viewDidUnload here so all releasing of view resources is
        only done at 1 place. */
    [self viewDidUnload];
    //TODO: Release other owned ivars here

    [super dealloc];
}

- (MainView*)mainView {
    if (!mainView) {
        /* We use [UIScreen mainScreen].bounds instead of
            self.view.bounds so we don't create self.view as a
            side-effect. It's messy because creating self.view will
            trigger creation of self.mainView itself */
        self.mainView = [[[MyView alloc] initWithFrame:
            CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width,
            [UIScreen mainScreen].bounds.size.height)] autorelease];
    }

    return mainView;
}


@end

And here’s the corresponding view class:

@interface MyView: UIView

@end


@implementation MyView

- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        //TODO: assemble your subview and add them to self.view
    }

    return self;
}

@end

I often build a UIViewController and UIView subclass containing common code which serves as the base class for most view controllers and views in an application.