Benedict's Soapbox

Loading a UIView subclass from a NIB

Nibs are great. They reduce the drudgery of view layout and allow us to work at a level of abstraction that matches the problem domain. But things aren’t all rosy. There are aspects of the implementation that leave room for improvement. The most common usage of nibs (in iOS at least) is to setup the view for a UIViewController. Some times, however, we want to re-use a specific subview between these nibs but it’s not obvious how best to do this. Here’s how to load a view from a nib so that the view can be instantiated in code and from other nibs.

  1. Create a UIView sub class and a nib file:
    Screen Shot 2013-11-19 at 20.21.18

  2. Add properties with IBOutlet annotations for the subviews in a class extension in the .m file:
    @interface BCDemoView ()  
    @property(nonatomic) IBOutlet UIImageView *imageView;  
    @property(nonatomic) IBOutlet UILabel *titleLabel;  
    @property(nonatomic) IBOutlet UILabel *subtitleLabel;  
    @end  
    
  3. Add an another property for a UIView container:
    @interface BCDemoView ()  
    @property(nonatomic) IBOutlet UIImageView *imageView;  
    @property(nonatomic) IBOutlet UILabel *titleLabel;  
    @property(nonatomic) IBOutlet UILabel *subtitleLabel;  
    @property(nonatomic) IBOutlet UIView *container;  
    @end  
    
  4. Redeclare the subviews you wish to expose publicly in the header file:
    @interface BCDemoView : UIView  
    @property(nonatomic, readonly) UIImageView *imageView;  
    @property(nonatomic, readonly) UILabel *titleLabel;  
    @property(nonatomic, readonly) UILabel *subtitleLabel;  
    @end  
    
  5. Configure the nib so that:
    • the file owner is an instance of your class:

    • the base UIView is connected to the container property of the file owner:

    • the remaining outlets are connected to subviews of the container view:

  6. Implement initWithFrame: (used when creating the view in code) and initWithCoder: (used when creating from a nib) to load the view from the nib:
@implementation BCDemoView  
-(id)initWithFrame:(CGRect)frame  
{  
    self = [super initWithFrame:frame];  
    if (self == nil) return nil;  
    [self initalizeSubviews];  
    return self;  
}

-(id)initWithCoder:(NSCoder *)aDecoder  
{  
    self = [super initWithCoder:aDecoder];  
    if (self == nil) return nil;  
    [self initalizeSubviews];  
    return self;  
}

-(void)initalizeSubviews  
{  
    //Load the contents of the nib  
    NSString *nibName = NSStringFromClass([self class]);  
    UINib *nib = [UINib nibWithNibName:nibName bundle:nil];  
    [nib instantiateWithOwner:self options:nil];  
    //Add the view loaded from the nib into self.  
    [self addSubview:self.container];  
}

@end  

A few things to note