75
votes

I have a UIView called baseViewand in that I have initWithFramewhere I add some other views and do some custom stuff. The same view also has a NIB file.

Now I have a UIViewController class named AppController in which I want to add the baseView view to the view of the AppController view so I am doing this:

self.view = baseView; but the problem is that the NIB file does not get loaded. How do I make sure the customized stuff AND the NIB file get´s loaded/run?

5

5 Answers

168
votes

You have many options, depending on how your "baseView" class is meant to be used and integrated in to your application. It's not clear just how you intend to use this class -- as the view in a UIViewController subclass, or as a reusable modular component mean to be instantiated multiple times throughout your application, for use in many different view controllers.

If your view is meant to be the only view in a UIViewController subclass, then Phonitive is correct -- bundle it together with the UIViewController subclass .xib file and use the UIViewController's viewDidLoad to do final initialization.

But if you want your View class to be a subcomponent reused multiple times in different view controllers, integrated either via code or via inclusion in a .xib file for another controller, then you need to implement both the initWithFrame: init method, and awakeFromNib, to handle both cases. If your internal initialization always includes some objects from .xib, then in your initWithFrame you'll need to load your .xib manually in order to support "customer" classes that want to create your widget via code. And likewise, if a .xib file contains your object then you'll need to make sure you call any code-required finalization from awakeFromNib.

Here's an example of how to create a UIView subclass component with the UI design in a nib.

MyView.h:

@interface MyView : UIView
{
    UIView *view;
    UILabel *l;
}
@property (nonatomic, retain) IBOutlet UIView *view;
@property (nonatomic, retain) IBOutlet UILabel *l;

MyView.m:

#import "MyView.h"
@implementation MyView
@synthesize l, view;

- (id)initWithFrame:(CGRect)frame 
{
    self = [super initWithFrame:frame];
    if (self) 
    {
        // Initialization code.
        //
        [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil];
        [self addSubview:self.view];
    }
    return self;
}

- (void) awakeFromNib
{
    [super awakeFromNib];

    // commenters report the next line causes infinite recursion, so removing it
    // [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil];
    [self addSubview:self.view];
}

- (void) dealloc
{
     [l release];
     [view release];
     [super dealloc];
}

Here's what the nib file looks like (except that file's owner needs to be changed to MyView class).

enter image description here

be sure to hook up both the view and label outlets to File's Owner. That's it! A template for creating re-usable UIView widgets.

The really neat thing about this structure is that you can place instances of your MyView object in other nib files, just place a UIView at the location/size you want, then change the class in the identity inspector (CMD-4) to MyView, and boom, you've got an instance of your widget in whatever views you want! Just like UIKit objects you can implement delegate protocols so that objects using your widget can be notified of interesting events, and can provide data to display in the widget to customize it.

3
votes

I found this post after having a problem trying to do this in my app. I was trying to instantiate the view from a NIB in the ViewDidLoad method, but the controls acted as if they were disabled. I struggled with this trying to directly set the userInteractionEnabled property and programmatically set the touch event selector for a button in this view. Nothing worked. I stumbled upon another post and discovered that viewDidLoad was probably too soon to be loading this NIB. I moved the load to the ViewWillAppear method and everything worked. Hope this helps someone else struggling with this. The main response was great and works well for me now that I have it being called from the proper place.

2
votes

if you want to use a NIB, it's better for your UIView to be linked with a UIViewController, in this case you can use

UIViewController *vc=[[UIViewController alloc] initWithNibName:@"YourNIBWihtOUTEXTENSION" bundle:nil]

[self.view addSubView:vc.view ];

becareful of memory leaks, you have to release vc

0
votes

If you have a custom UIView with a xib file.

- (id)initWithFrame:(CGRect)frame
{
   self = [super initWithFrame:frame];

   id mainView;
   if (self)
   {
       NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"HomeAllAdsView" owner:self options:nil];
       mainView = [subviewArray objectAtIndex:0];    
   }
   return mainView;
}

- (void) awakeFromNib
{
   [super awakeFromNib];
}
0
votes

This post helped me Building Reusable Views With Interface Builder and Auto Layout. The trick had to do with setting the IBOutlets to the FileOwner and then adding the content view to itself after loading the nib