Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"File's Owner" of a custom XIB

If I create an individual nib file and load it in my viewcontroller and assign it to an ivar like so:

self.loadingview=[[[NSBundle mainBundle] loadNibNamed:@"loading" owner:self options:nil] objectAtIndex:0];

Is it possible to create IBOutlets to any thing on it in the nib, like activity indicators? Would File's Owner be the viewcontroller, even though IB wouldn't know this, since it is made an object in code?

Is loading the nib the way I've done it the acceptable practice?

Perhaps the more acceptable practice is to also create a .h/.m, and use that as the class for the XIB, in which case File's Owner is the new custom class; i'd still need to use the above method in my controller, though, when creating the ivar from the custom class, right? Or is there a better way to do all this, without using the controller's xib?

UPDATE: Based on some answers, I did the following. I got rid of the NSBundle method above, and instead just do this:

@property (nonatomic, retain) IBOutlet UIView*loadingview;

In the nib file for this view, I click on File's Owner and change the Custom Class to my viewcontroller. Then I control-drag from File's Owner to the View and it links up fine.

I would assume at this point that I now have an ivar in my controller, self.loadingview that contains the information in the nib. However this isn't the case. I removed the NSBundle method, assuming the controller will now load the nib directly through the IB outlet, but the view in the nib does not show up and I have no control over it in the controller.

I had also removed this when I removed the NSBundle method:

[self.view addSubview:self.loadingview];

I'm not clear how the view actually displays at this point; and if it does, where in the view heirarchy it appears? perhaps it is behind other views. I tried this too:

[self.view bringSubviewToFront:self.loadingview];

But the view just isn't there. In an effort to find it, I did:

//[self.view bringSubviewToFront:self.loadingview];

NSLog(@"index: %i",[[self.view subviews]indexOfObject:self.loadingview]);

both with and without the commented part. I get this:

index: 2147483647

like image 676
johnbakers Avatar asked Aug 25 '11 13:08

johnbakers


3 Answers

Just because you have created a nib and inside that nib hooked up the outlet on File's Owner does not automatically load that nib from your view controller.

You can use the UINib class for this:

UINib* nib = [UINib nibWithNibName:@"loading" bundle:[NSBundle mainBundle]];
[nib instantiateWithOwner:self options:nil];

Once you do instantiateWithOwner:self, the nib will be loaded and self will be used as the File's Owner and the view from within the nib will be connected to your loadingView property.

You still need to call [self.view addSubview:self.loadingView] to add it to the screen.

like image 191
Hollance Avatar answered Sep 20 '22 23:09

Hollance


Interface Builder will know of the file's owner that you sent through the owner:self argument. What you need to do to make your IBOutlets show up in Interface Builder is to set the correct class of the File's Owner in IB under the Identity and Type tab.

like image 30
PeyloW Avatar answered Sep 21 '22 23:09

PeyloW


Is it possible to create IBOutlets to any thing on it in the nib, like activity indicators? Would File's Owner be the viewcontroller, even though IB wouldn't know this, since it is made an object in code?

They wouldn't be IBOutlets, but yes, you can wire connections by hand. IBOutlet is an empty macro. It exists exclusively so that Interface Builder can find the properties you would like it to wire to. But the IBOutlet keyword has absolutely no impact on the runtime (it's not even seen by the compiler). Similarly IBAction is a macro that is replaced with void. Nothing about Interface Builder exists at runtime.

When the nib loader reads the nib file, it deserializes each object and then for each connection, it sends a setter message (setFoo:) to the connection target passing the new object. After it has finished that for every object in the nib file, it sends awakeFromNib to every object it created. Objects with a connection target of nil are sent to the "owner" (which is self in your example). Note that the file owner is not created by the nib loader. The nib loader has no control over the actual class of the file owner. It's up to you to make sure that the file owner responds to the set...: calls or you'll crash.

Is loading the nib the way I've done it the acceptable practice?

I would not rely on loadingView being the first top-level object. The order of objects is not defined. Instead, you should use IB to wire the view to the loadingView property of File Owner.

Perhaps the more acceptable practice is to also create a .h/.m, and use that as the class for the XIB, in which case File's Owner is the new custom class; i'd still need to use the above method in my controller, though, when creating the ivar from the custom class, right? Or is there a better way to do all this, without using the controller's xib?

The traditional way to do it is to have a UIViewController for each nib file in iOS. But that's isn't required. On Mac, NSViewController is pretty new and doesn't work as well as UIViewController, so there isn't as strong a tradition there. Hand-loading like I'm describing is more common there, though most things are still handled through NSWindowController.

If you want to do these kinds of things, Nib Files in the Resource Programming Guide is required reading.

like image 30
Rob Napier Avatar answered Sep 17 '22 23:09

Rob Napier