11
May
2009
 

Magical iPhone View Controllers

by Matt Long

Update: This is documented behavior.

Every now and again while doing development you stumble upon something that makes you go, hmmmm. Those are normally the moments at which you have to ask yourself, “is this a bug or a feature”. If it’s a bug, then you should file a radar with Apple, however, what if it’s a feature? You blog about it, of course!

I have done a bit less iPhone development than Marcus, so he was a little stumped while looking through some of my code where I created a view controller using a simple alloc/init. Most interestingly is that fact that the app works. It loads the correct nib and displays the view just fine without any trouble. Notice I said alloc/init and not alloc/initWithNibName. How can this possibly work? How did my controller “know” which view to use?

Well, the answer is pretty simple, but not immediately obvious. I could try to act like I meant to use an undocumented feature because I’m just that cool, but the fact of the matter is I was in band. Marching band, even. Band people aren’t cool. Though the color guard thought we were cool. But I digress. I just inadvertently added the alloc/init code without thinking and never looked back since the view loaded and displayed just fine. After renaming the nib/xib in the project and in doing so causing the view to fail to load properly we were able to determine that there must be some underlying feature of view controllers where it will automagically look for a view based upon its own name.

Say Huh?

Yep. That’s correct. If you create a view controller with the name MySpiffyViewController, for example, and instantiate it with alloc/init in your code, it will assume that the nib you want is actually called MySpiffyView. Spiffy eh? Some people may call this a bug, but the fact that it works this way shows that it was intentional. Now, I recommend that you steer clear of intentionally creating your view controllers this way as Apple may decide it’s a bug after all and change (fix?) it in a future release. It is however an interesting undocumented feature at the moment.

You Can Duplicate It

Here are the steps if you want to try this yourself. Of course, you can just download the example project below if you don’t want to go through the steps.

  1. In Xcode, press Command+Shift+N to open the New Project templates and select View-based Application in the iPhone OS category and click Choose….
  2. Name the project SpiffyView and click Save
  3. The first thing we need to do is change our code in the app delegate (called SpiffyViewAppDelegate.m) to add a UINavigationController.
    - (void)applicationDidFinishLaunching:(UIApplication *)application {    
        UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
        [[navController navigationBar] setBarStyle:UIBarStyleBlackTranslucent];
        [window addSubview:[navController view]];
        [window makeKeyAndVisible];
    }
    

    This will enable us to navigate to the new view we create next.

  4. Press Command+N and select View XIB under the User Interfaces category. Click Next. Name the file MySpiffyView and click Finish
  5. Press Command+N again and select UIViewController subclass under the Cocoa Touch Classes category. Click Next. Name the file MySpiffyViewController.m and click Finish.
  6. Next we need to create an action in the view controller created by the project template called SpiffyViewViewController. Add the following code to the respective .m and .h files as follows.
    // Add this to your SpiffyViewViewController.h
    - (IBAction)showSpiffyView:(id)sender;
    
    // Add this to your SpiffyViewViewController.m. Make sure you #import "MySpiffyViewController.h" as well.
    - (IBAction)showSpiffyView:(id)sender;
    {
        id controller = [[MySpiffyViewController alloc] init];
        [[self navigationController] pushViewController:controller animated:YES];
        [controller release], controller = nil;
    }
    
  7. Now we need to connect a button to the action. In your project under resources, double-click SpiffyViewViewController.xib to open it in Interface Builder.
  8. In Interface Builder, drag a button onto the view and change its text to “Spiffy View”
  9. Now, Control-Click and drag a connection from your button in the view, to the File’s Owner and select showSpiffyView: in the ensuing menu. Save the file in IB.
  10. Now, back in Xcode, double click MySpiffyView.xib to open it in Inteface Builder.
  11. Change the File’s Owner class to MySpiffyViewController in the Identity tab of the Inspector.
  12. Drag a label onto the view. Double click it to edit it and change the text to “This is the super spiffy view.” Save the file in IB.
  13. Everything should be hooked up now. Go back to Xcode and “Build & Go”. Your app should load in the simulator. When you see the “Spiffy View” button, click it and it should show your new view!!

Conclusion

So how is that working? The only answer is there must be some underlying code that the view controller is using to locate its nib. Remember, we never told our controller what nib to use. We simply called alloc/init and voila, it works!!

If you think I ought to register this as a bug with Apple, let me know your thoughts in the comments. Oh and one other thing. I may have been in band, but I married a smokin’ hot cheerleader. Take that all you cool people. ;-) Until next time.


Example Project SpiffyView.zip

Comments

apinske says:

Yeah. That’s absolutely brilliant behavior. I have often written this on my own, i.e. make the init method call supers initWithNibName:bundle: because i hate it when the class instantiating my view controller, e.g. its parent, … needs to know how the nib is named. In my opinion this should be a well hidden private implementation detail and not a parameter.

My 2 cents.

claas.lange says:

Maybe the View Controllers knows about the files it “owns”. Since you set the File’s Owner property in IB, this could be an explanation. Or it’s just clever naming conventions.

Matt Long says:

@claas.lange

I had a similar thought at first, but you’re talking about the wrong direction. How can the code know which resource to use if you don’t specify it with alloc/initWitNibName? Also, if you change the name of the xib filename, the view no longer loads properly.

Best regards,

-Matt

Andy says:

Well, well. Two different friends noticed similar magic, and after poking around I noticed when you have a view controller in a nib, you can set its nib name property. See the view controller in MainWindow.nib, for example. So I thought there wasn’t really magic after all.

But this seems to prove there *is magic, since there’s nowhere in any nib file where MySpiffyView can be specified as the nib name, as far as I can tell.

chevol says:

This is in the Apple Documentation as well as in a couple books like Erica Sadum’s.

Matt Long says:

@chevol

Can you provide a link to said Apple documentation? Would love to post it in the article.

Thanks.

-Matt

edog1203 says:

Documented as of iPhone OS 3.0, but works on 2.x (not sure what version, exactly) as well.

http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instp/UIViewController/nibName

For a view controller titled MyViewController, it will look for MyViewController.nib as well as MyView.nib (in that order).