views:

837

answers:

2

Hi all,

I've finally had to give up the relentless search for an answer to this question, as I just can't find anyone that's asked it before! So hope someone can provide some insight. I'll start by explaining what I can do, then compare that with what I can't figure out how to do.

Suppose I have a custom VC called RootViewController. It contains an outlet of type MyViewController. My RootViewController has an .xib which contains a generic VC object dragged out of IB's palette which is given the class type of MyViewController, I set the right bar button item to a UIBarButtonItem called 'Cancel'. The RootViewController unarchives it's .xib, hooks up the outlet to the MyViewController object, I push said object on to the navigation stack which displays as expected, I have a view and a button on the navigation bar that says 'Cancel'.

The problem I have with this approach is that I want MyViewController to be re-usable across any object that might want to create a MyViewController. This therefore means that the "File's Owner" could be any object. Previously it was RootViewController, but what if another object wanted to instantiate a MyViewController? The Files Owner will be different every time. I also want it to be able to completely initialise itself from a nicely self-contained .xib. Basically, I want to do this:

MyViewController *myVC = [[MyViewController alloc] init];

And in the implementation of MyViewController, I write this:

- (id)init
{
    if ( self = [super initWithNibName:@"MyViewController" bundle:nil] )
    {
        // Initialisation
    }
}

This neatly hides the name of the .xib used to intialise the VC, uses all the goodness IB gives me in configuring the controller and view, and with MyViewController always being the owner, it means I solve the File's Owner problem too - it will work for any type of class that creates it.

So, in order to achieve this, I create a .xib, set the File's Owner to be of type MyViewController, drop in a UINavigationItem and add the UIBarButtonItem. I now have a .xib structurally the same as before, except it does not use IB's generic VC object as separate top level object, the .xib is the VC definition, rather than something that contains a VC definition.

So, given that File's Owner is a MyViewController and as such a subclass of UIViewController (supposedly inheriting everything you get by using a UIViewController from IB's palette), then I should inherit all the functionality of it's superclass... Except it doesn't.

When the .xib in unarchived, it does not hook up the UINavigationItem. Therefore, when it's pushed on to the navigation stack, none of the bar button items are displayed.

UIViewControllers navigationItem property is read-only, creating an outlet for it in iPhone OS 3.0 is therefore deprecated.

So, at the end of all this, how on earth does the nib loading code manage to connect IB's version of the VC object to the navigation item? And why, even though my object is a UIViewController through inheritance, will it not do it for my object? I am completely at a loss to fathom this out...

Thanks for reading this far! Hope to hear from you guru's

A: 

Only a UINavigationController sets UIViewController's navigationItem property. Do something like this:

MyViewController *viewController = [[MyViewController alloc] init];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];

Assuming you are in a view controller that already has a navigation controller. After you have pushed it, you will be able to access navigationItem from inside MyViewController.

Sam Soffes
Hi Sam, I'll double check what I do when I get home, but I'm almost certain I do that already (from within a VC managed by a NC). Also, could you elaborate a bit more about what you mean when you say a UINavigationController sets a UIViewControllers navigationItem? As I understood it, a navigation item is an attribute of the UIViewController class, when the VC object is pushed on to the stack, the NC requests the navigationItem from the VC and displays it if one is provided. This would seem to make sense as in IB you embed the nav item inside IB's VC object.
Paul
Paul, if I remember correctly, if you make a UIViewController and don't add it to a UINavigationController, it's navigationItem will be nil. You are correct that the property is read-only. You don't have to set it. If the system isn't setting it, then something isn't getting setup correctly.
Sam Soffes
Hi Sam, I've taken a look at the code again and can verify I do as you suggest and it still doesn't work. I spent ages editing the post to include images of the problem and the set up of my .xib's only to discover I haven't earned enough points to post images! ARGH! How do I get some!?!
Paul
A: 

Right, I think I get it now. I thought I'd post an answer to my own question as I've seen at least one other person ask a similar question, and both seem to point to the same conclusion, so I hope this will be of help to others.

Essentially (and this will sound obvious) a UIViewController is not a Proxy Object! It's not instantly obvious as we're all used to the idea that if any two objects inherit from the same base class, then their implementations are the same and they will behave in exactly the same way (assuming no customisation in the inheriting class). But an IB object's type is distinct from the class attribute you can assign to these objects. They are not both UIViewController objects because their class attribute it set in such a way.

Simply setting the class attribute of these objects in IB does not mean that these objects are now UIViewController objects. A View Controller object remains a View Controler object, and a Proxy Object remains a Proxy Object. As far as IB is concerned, they are both very different beasts, they just happen to have the same class attribute.

Just take a look in to the .xib and you'll find your IB View Controller objects have been archived like this:

<object class="IBUIViewController" id="...">
    ...
</object>

And the Proxy Object (that is set to a subclass of a UIViewController) is archived like this:

<object class="IBProxyObject" id="...">
    ...
</object>

As you can see they are both very different types, one is a IBUIViewController and the other is a IBProxyObject - and then it starts to makes sense - you can't impose VC controller attributes on an object of type IBProxyObject.

It is also interesting to note the class type is an IBUIViewController object and not just a UIViewController object. Whether this is just a naming convention or not I don't know, but it could also imply that IB's view controller objects wrap the instantiated VC object, or is a factory object for it. For example, you can set a "Resize View From NIB" attribute in IB's view controller object - but I can find no equivalent property or methods in the UIViewController reference docs.

So in conclusion, if you're trying to instantiate an object programatically instead of using outlets to an IB object, be prepared to implement some of the initialisation that the IB version would otherwise provide for you...

Paul