views:

1081

answers:

3

I found a strange behavior, and would like to be explained what assertion I am making that is wrong.

In an AppDelegate class of a freshly created WindowBased project, I am adding a UIViewController to the window.
I can do it two different ways:
- with an IBOutlet. In IB, I simply instanced an UIViewController, set its class to TestViewController and connected it (scenario A of the code).
- creating the UIViewController with code (scenario B).

    - (void)applicationDidFinishLaunching:(UIApplication *)application {    

#define USE_IBOUTLET YES // Comment this line to switch to scenario B

#ifdef USE_IBOUTLET
    // Scenario A

    [window addSubview:theTestViewController.view];
    [window makeKeyAndVisible];
#endif


#ifndef USE_IBOUTLET
    // Scenario B

    TestViewController *theTestViewControllerProgrammatically;

    theTestViewControllerProgrammatically = [[TestViewController alloc] initWithNibName:nil bundle:nil];

    // According to Apple: "It is a good idea to set the view's frame before adding it to a window.", so let's do it
    [theTestViewControllerProgrammatically.view setFrame:[[UIScreen mainScreen] applicationFrame]];

    [window addSubview:theTestViewControllerProgrammatically.view];

    [window makeKeyAndVisible];
#endif
}

As I did not do any customization of the object in IB, I should have the same behavior in both scenario.

Scenario A, using the IBOutlet works as expected.
But the scenario B has the following problems:
- The view is not at the right position (20 pixels to high, and covered by the status bar).
- The view doesn't resize properly (for example, try to toggle the In Call Status bar)

Why?

Zip archive of the project here if you want to reproduce the problem: http://dl.dropbox.com/u/1899122/code/ProtoWindowBasedStrangeness.zip

A: 

Probably in case B that view is not aware of the presence of a status bar. You need to resize it accordingly and adjust its position to take the status bar into account. That is done by changing the frame (size) and bounds (location) properties of a UIView.

willcodejavaforfood
But why? What is different between creating an object in IB, and get it via the IBOutlet mechanism than creating the same object in code? There is absolutely no customization of the object done in Interface Builder.
Guillaume
+1  A: 

Hi Guillaume,

I have been having a very similar problem to you in that I noticed VC objects are not all created equal! The problem I'm having is setting the navigation bar items, I just can't seem to do it when File's Owner is a view controller object that I instantiate programatically. It only works if I unarchive IB's controller objects.

I downloaded your project and had a play around with it, and it got me thinking some more about what might be going on. I think I can provide a reasonable answer, but not sure if there is a simple solution...

What I believe is going on is that Apple have created these controller objects in IB that are slightly more specialised. One suggestion this might be true is that IB VC objects have an attribute you can set that has no direct corresponding property for a UIViewController class that I can see, so IB's controller objects may have some additional functionality that non-IB UIViewController subclasses can't take advantage of. Given that objects in an .xib are complete 'freeze-dried' objects, Apple may have included all kinds of private attributes we can't see or use in their IB versions of them - this may have some effect on how the objects are initialised.

For example, in your MainWindow.xib, select the IB VC object and you can set attributes on it from the Inspector Palette, such as "Resize View From NIB". If you un-check this and re-run your app, you'll see the VC appear exactly as it does in scenario B. As you can't check this item when from the File's Owner attributes (even though it is as a UIViewController), you're unable to take advantage of whatever is being done by the view controller to give you the behaviour you want.

The result of this is that when you use TestViewController.xib to initialise your VC object in code, none of the IB specific attributes of a VC are set, therefore a bog-standard UIViewController is created, and so things like the "Resize View From NIB" attribute and setting up the navigation items have to be implemented yourself.

I've not yet found a way to take advantage of the functionality that IB's view controllers have when I instantiate them using initWithNibName:bundle:nibBundle (I'm guessing it's all private stuff we can't access), but hopefully this might have given you a starting point...

Of course, I could be completely wrong and someone will make me look like a complete idiot!

Paul

Paul
I think I've sussed out the issue, if you're interested, take a look at the answer to my own question to subclassing UIViewControllers.
Paul
Paul, thanks for the pointer to your question. In your answer, you finish by "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...". What would such a code be in this specific context?
Guillaume
A: 

Hi Guillaume,

This is going to sound really silly after my long-winded answers, but the problem you're having is simple to fix (programatically).

This line:

[theTestViewController.view setFrame:[[UIScreen mainScreen] applicationFrame]];

Should actually be:

[theTestViewControllerProgrammaticaly setFrame:[[UIScreen mainScreen] applicationFrame]];

You were setting the frame for the VC set by IB, not by the one you created programatically.

Anyway - it's worth noting that all my comments still apply! There are still a few things you'll have to do programmatically if you don't use IB's controller objects (for example, setting up the navigation bar items)

Paul

Paul
Correct. Nice catch :) I edited the question and the content of the zip archive linked in the question.
Guillaume