75

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?

peterh
  • 11,875
  • 18
  • 85
  • 108
dbrasco
  • 1,183
  • 1
  • 12
  • 22

5 Answers5

168

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.

Bogatyr
  • 19,255
  • 7
  • 59
  • 72
  • 1
    Usually when i create an app i like to create some views and i want to use those views in an View Controller. I don't want to create a view controller for each view so that's why i create separate views which i want to include in a View Controller´s view. – dbrasco Feb 22 '11 at 13:22
  • 2
    I like the fact that you say i can implement initWithFrame and awakeFromNib but i am not sure what kind of initialization i have to make and in which order. Could you give an example ? Let's say i ahve added some labels and buttons in the XIB with IB. I have some IBOutlets in my View. Now i want in initWithFrame to do something with those object. How would i initiate the view with the NIB so it knows about those objects ? – dbrasco Feb 22 '11 at 13:25
  • If you only ever load a view from nib you never need initWithFrame. The only point of initWithFrame is for you to create the object in code and give it an initial frame (location and size within the superview). You put any "extra" initialization that your object needs in awakeFromNib (or your init routine if you create from code). I'll put a small example in an edit in my answer – Bogatyr Feb 22 '11 at 14:15
  • Welcome! Glad to help. Once I had this "epiphany" it made a real difference in how I structure iOS apps. – Bogatyr Mar 09 '11 at 08:24
  • 2
    Indeed. Granted, there are several cases where I can simply push another VC+view onto the nav controller, but that's not always the case. Now I need to re-read about IBPlugins, f'rinstance: http://cocoawithlove.com/2009/07/custom-views-in-interface-builder-using.html (wait, no, not iOS-specific, but still ...) – Joe D'Andrea Mar 09 '11 at 14:14
  • Yes I've seen that article -- haven't had the need to dive that deep yet, I'm sure I will some day... – Bogatyr Mar 09 '11 at 14:31
  • This is a good solution to the problem. I found that passing the following properties through to the view in the init/awake was also useful: self.view.frame = self.frame; self.view.autoresizingMask = self.autoresizingMask; – Willster Mar 09 '12 at 12:42
  • 5
    loadNibNamed inside awakeFromNib causes endless recursion – Gargo Oct 31 '12 at 07:37
  • @Gargo: Haven't noticed this problem, I'll verify the next time I'm working on iOS stuff. – Bogatyr Oct 31 '12 at 11:49
  • 1
    It's true, @Gargo is correct, I just testing this code (Great answer) but I was entering an infinite loop on initialisation, after removing the loadNibNamed inside awaleFromNib this problem stopped. Thanks. – Ospho Nov 24 '12 at 08:11
  • @Oliver thanks for the confirmation, I'm not able to test this at the moment. I'll update the answer to remove that line. It makes sense I suppose, since you're already loading from nib you don't need to load the nib again. Admittedly I don't use that path much, so thanks for the fix! – Bogatyr Nov 25 '12 at 09:40
  • How do I send a NSArray with my init call. Should I make my own init method? – Morten Gustafsson Mar 13 '13 at 12:28
  • if you do like this, you have created two `MyView` . one from `initWithFrame:` one from Nib, the one from Nib are added to the one from `initWithFrame:` – aelam Dec 19 '13 at 05:20
  • @aelam have you read and understood the issue this solves? The multiple views are necessary (as far as I can tell) for the benefits this approach provides. By all means, feel free to post your own complete solution to the OP. – Bogatyr Jan 27 '14 at 19:48
  • In my case [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil]; is necessary if I want to make the whole thing work. Without it nil is assigned to self.view. EDIT: this comment is still valid, but you should read my edit of the answer; the "IMPORTANT: ..." paragraph. – Lukas Kalinski Apr 03 '14 at 07:42
  • I could not live a normal life knowing there are two instances of the view class there. I ended up getting rid of the XIB and doing it old school, straight code! – pnizzle Jun 26 '14 at 05:22
  • 1
    Are the IBOutlet properties set to "retain" because the view is the owner of itself? – Oren Jun 01 '15 at 23:46
  • UIViews do not have a `view` property, so the `[self addSubview:self.view];` line does not work. Maybe it was supposed to be something like this? `NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"SubscriptionPrompt" owner:self options:nil]; [self addSubview:subviewArray[0]];` Maybe there used to be a view property on UIViews. – ftvs Mar 09 '16 at 04:12
  • @ftvs check the header file again. – Bogatyr Mar 09 '16 at 15:11
  • Huh. That means MyView is the file owner and also references the view through the IBOutlet. `[[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil];` then sets up the `view` property, which is then added to the subview. MyView (created with initWithFrame) will have a subview of MyView (created with loadNibNamed, and loaded into the view property). – ftvs Mar 10 '16 at 04:11
3

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.

crissag
  • 49
  • 2
  • 1
    Please share the link to this other post with us. –  Aug 10 '12 at 21:56
  • He probably ment http://stackoverflow.com/a/819265/779419 . When instantiating the custom view, make sure to actually use initWithFrame with a proper CGRect. – schieferstapel Mar 11 '13 at 12:06
2

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

Ilanchezhian
  • 17,426
  • 1
  • 53
  • 55
Phonitive
  • 120
  • 3
  • Not true; If you need to alter a UIView's contents using the Interface Builder you will need to use Bogatyr's method. The reason why you would need to use UIView in conjunction with Nib is that Apple discourages use of nesting UIViewControllers because parent calls can break. That is if your supporting < iOS 5. http://stackoverflow.com/questions/1141015/is-it-wise-to-nest-uiviewcontrollers-inside-other-uiviewcontrollers-like-you-w – Ospho Nov 24 '12 at 08:20
  • This isn't a great implementation - i'd research container vcs – Adam Waite Aug 14 '13 at 20:37
0

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];
}
felixwcf
  • 2,078
  • 1
  • 28
  • 45
0

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

Maria
  • 4,471
  • 1
  • 25
  • 26