Technology Musings

Making Reusable iPhone Widgets with Interface Builder

JB

One annoying thing about Interface Builder, at least for the iPhone, is that there is not a direct way of making reusable widgets that are both made *in* Interface Builder and *for* Interface Builder.  I came up, though, with a set of instructions and helpers that make the process fairly painless.  The class is called IBView, and should be used as the parent class for your reusable widget.

Here's how to create the widget:

  1. Create a class for your new widget, called MyWidget.  Inherit from the IBView class (code provided below).  Create all of the IBOutlets you want for your widget.
  2. Create a view with the same name as your widget class in Interface Builder.  If your widget was named "MyWidget.m" then your interface should be called "MyWidget.xib".  DO NOT set the class of this view or any subview to be of the MyWidget (or whatever class you are create) type.  DO NOT do it.
  3. Set the "File's Owner" of your interface in Interface Builder to be the name of the class that you are creating - MyWidget in this case.
  4. Set up any connections you want between your IB view and File's Owner (i.e. your widget).

Now, your widget is created.  You can now use the view in any IB panel by doing putting in a generic UIView, and setting the class name to your new class (MyWidget in this case).

Important caveat - one consequence of creating the view in Interface Builder is that there will be an extra view between the main view represented by your class and the rest of the elements of your class.  Basically, your custom view acts as a generic view, and then *loads in* your Interface Builder file as a subview, meaning that the toplevel view in Interface Builder will sit under your MyWidget view.  This is usually unimportant if you are just using IBOutlet stuff, but if you do advanced view management, it would be important to know that a view sits between you and the rest of your sub-widgets.

Okay, here is the code for IBView.m - just very simple code, with a tiny bit of Objective-C magic thrown in:

#import "IBView.h"

@implementation IBView

-(void) loadViewsFromBundle {
NSString *class_name = NSStringFromClass([self class]);
NSLog(@"Loading bundle: %@", class_name);
UIView *mainSubView = [[[NSBundle mainBundle] loadNibNamed:class_name owner:self options:nil] lastObject];
[self addSubview:mainSubView];
}

-(id) initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if(self) {
[self loadViewsFromBundle];
}
return self;
}

- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self loadViewsFromBundle];
// Initialization code.
}
return self;
}

@end

And IBView.h is super-simple:

#import <UIKit/UIKit.h>
@interface IBView : UIView {
}
@end

And that's all there is to it!