Channels ▼
RSS

Mobile

The iPhone Isn't Easy


It's All About Controllers

As I brought the details of implementing UI controls into sharp focus, I discovered that while I'd be still have the app add the views to a window, there was an intermediary involved: a UIViewController object. This object, known as a view controller, supports the OOP-based model-view-controller (MVC) scheme used by iPhone apps. In this program design, a model object manages the app's data and implements its custom logic, while the view displays the model's data. The controller acts as an intermediary that manages user interactions detected by the view, and has the model respond to them, either by editing or updating its data content. In addition, the controller then has the view refresh its content with the model's revised data. MVC keeps the app's unique data and behaviors within the model object and separate from the view assigned to display its data. Can you say encapsulation?

My planned app had only one screen where the spaceship would appear, plus a help screen. This arrangement could be neatly managed by a single instance of the generic UIViewController class, which would implement the physics model and jump to the help screen and back. While it's possible to implement all of the app's logic within a view object or in other ways, I found it better to follow the MVC design pattern that the Cocoa Touch frameworks support.

Unlike the UIApplication class that requires you edit a delegate object, the UIView and UIViewController classes can be sub-classed so that you can extend their behavior with custom code. The custom code in the view controller would receive touch events from the view representing a virtual control pad, and convert them to commands that would affect the physics of the spacehip. The controller would then update the position and appearance of the subview displaying the spaceship. To recap the program design, both the control pad and spaceship would be subviews within the background view. This view in turn would be attached to the mother-of-all views, the window object. The program's design was coming together.

Apple's XCode development tools, as mentioned previously, provide ready-to-use iPhone application templates to help get you started. I launched XCode, and in its New Project dialog, chose the Cocoa Touch frameworks, and then chose an app template that was window-based. This I named SpaceAppDelegate. While there were templates that generated a view-based app template, I wanted to add the view controller and view classes as I went.

Next, I added a view controller class. This was just a matter of selecting New File in XCode, followed by choosing the UIViewController class from the dialog. I named my view controller SpaceViewController. XCode generates the basic header file and method file for the class. The latter has stub routines for some of the class methods, allowing you the opportunity to add custom code.

The first thing the view controller would do is generate a view that covered the app's window and display an image of a star field as a background. Since the background was a static image, I decided that storing the view and its image in a nib file would be a good idea. It was, but getting a properly made nib file that didn't crash the app (the phone itself wasn't affected) was a major problem until I sorted things out.

Interface Building

To make nib files for your app's visual interface, you use the appropriately named Interface Builder program, often just called "IB". It provides a number of preconfigured objects such as UIImageViews (that display images), scrolling views, buttons, sliders, and other visual controls. You drag and drop these objects from IB's library window onto a mockup of the view to assemble the app's UI. IB thus allows you to design the visual elements of the app's UI without worrying about its underlying code. This fits nicely into the MVC design pattern, as IB keeps most of the UI elements uncoupled from the app code.

However, IB does much more than assemble an app's UI. The visual elements that you place into a view's layout become full-blown objects when they are loaded. (Apple characterizes the information stored in the nib file as "freeze-dried objects".) Put another way, the XML generated by IB that describes the object and its properties can be reconstituted at run time into actual UIKit objects. For example, that button you place in a view becomes a UIButton object that can detect a touch event on it. Furthermore, it responds by highlighting itself — no action on the part of the programmer is required. More important, the button object can send one of many pre-defined messages to the application. In short, when you use IB to put together the app's UI, you are not only constructing its visual interface, but also its underlying event and messaging apparatus that communicates with the program.

IB lets you route the messages generated by these UI elements to specific destinations, which in my case are methods in SpaceViewController. This is accomplished by clicking and dragging a link representing a message generated by the UI object to the name of a method in the receiving object. For example, for the view representing a button, you Control-click on it, and a menu of possible messages that can be generated in response to the action appear. You next click on the desired message link and drag it to the File's Owner icon, which represents the view controller receiving the messages; see Figure 3. A pop-up menu of methods appears, and when you select one, it appears in the Connections browser window. The visual metaphor used to match UI messages to methods is a powerful one, and makes coupling the UI's actions to specific program responses easy to do. Having said that, IB's assignments aren't foolproof or bulletproof. Mistakes can be made, and I made a good one at the outset.

Figure 3

I started my foray into nib files by launching IB, and in the New File dialog selected a view, and named it SpaceView. I made it the size of the iPhone screen (320 x 480 pixels). From the library window, I dragged a UIImageView onto the view, and made it the same size. From the object inspector, I assigned UIImageView's content to be an image of the Horsehead Nebula that I had trimmed to the screen dimensions and saved in a file named Backdrop.png. With the view thus set up, the existing stub code in the view controller should be sufficient to load the nib and display the nebula image in the iPhone simulator. Displaying the image would make for a good initial test.

No such luck. My trial app died a horrible death, usually with an uncaught exception.

It took me a while to track down the culprit. In IB, if you botch specifying the object that a view sends its messages to, bad things happen. That's because Objective-C's message-forwarding and delegation mechanism builds the links between objects and methods at run time. Therefore, if you fail to specify say, a view controller as the message recipient, the result is that the iPhone OS loads a view with broken links. When the view attempts to pass messages to the view controller, they go spinning into oblivion, carrying your app with them.

The gotcha is in how IB sets the destination object for the view's messages. IB uses a placeholder object, called the File's Owner, to represent the destination object. You've met this object already, and can see it in the IB document window for SpaceView in Figure 3. Put another way, the File's Owner information designates the object that "owns" the interface, which means that it is the message recipient. Stated another way, the owner is the instance of the class that loads the nib. For my program, that would be SpaceViewController. Granting ownership isn't accomplished by pointing and clicking in IB as was the case with the other assignments. This is done by typing the class name into IB's Identity Inspector. I had overlooked doing this. Once I entered the class name and rebuilt the project, the background image of the nebula appeared in the iPhone simulator. I was one step closer to my goal. For more information on IB's pitfalls and how to have problem free interface construction work, see the sidebar, "IB Tricks and Traps".

IB Tricks and Traps

Interface Builder (IB) is valuable for visually constructing an iPhone's UI and choosing the messages that it generates. However, like any software tool, mistakes in construction can result in the app crashing. The UI objects, their attributes, and message assignments are buried in either in .xib file (a text file consisting of dense XML that's generated for development purposes) or binary format (a .nib file that's generated for the release version of the app). These objects are loaded where you can't study or trace them, which makes trouble-shooting difficult. Generally, if the debugger informs you that it is terminating the application due to an uncaught exception, the problem is rooted in a view not loading properly, or due to a broken link in the nib file.

However, there are a number of ways you can safeguard your efforts, which I have fashioned into rules. These rules have been derived from my own experience.

  • Rule 1: When you make a view in IB, the first thing to do before all else is configure the File's Owner object. That is, you specify the object that is the recipient of the view's messages. Click on the File's Owner icon in IB's document window, and choose the Identity Inspector. In the Class field, enter the class's name (typically a view controller). Type-ahead in this field is supported, but be careful: if you have a lot of classes with similar names, a wrong keystroke could select the wrong one. Save the nib file with an appropriate name.
  • Rule 2: For view objects, be sure to assign an outlet. This is the inverse of the purpose described rule 1: you're assigning a variable in the File's Owner that points to the view. Control-click on the File's Owner icon in IB's document window, then drag a link to the view icon in this window. You should get a pop-up menu specifying a view instance. Choose it, and save the file.
  • Rule 3: Build your nib files from scratch. While you can learn valuable interface techniques by studying the nib files of example programs, reusing them is not a good idea. The reason is that as you change things to suit your app, you break the pre-existing links in the example program's nib. Unless you are very thorough, finding all of the broken links is difficult and tedious. You're better off spending the effort making your own nib files from the ground up. You'll learn more in the process as well.
  • Rule 4: If you change the name of a class in your source code, be sure to update the changes in IB as well as any source files. In particular, update the File's Owner object. See rule 1.
  • Rule 5: When loading a nib file in code, be sure that the nib file name you present the initWithNibName method matches the actual file name, or is the correct one. Where this burns you is when you copy and paste some code, and forget to modify the nib file name. While I find it useful to have the nib file describe its class (such as SpaceView for a view object), there's no requirement that the nib file name has to match anything.
  • Rule 6: Have XCode, along with your project, up and running as you build the nib file, as suggested by Apple. These two programs intercommunicate changes, which is handy as you modify or add methods that will be targets of messages sent by the UI. Sometimes one of these programs can miss a beat and the nib file falls out of sync with the app code. This can be avoided by performing a clean build once and a while.
  • Rule 7: When IB gives you a hint, take it. Occasionally as you re-arrange the UI's elements, you might notice a warning icon at the bottom of IB's document window. IB's telling you something isn't right. Click on the icon to get a nearly plain-English explanation of the warning, which should help you correct the problem.

Apple has an excellent tutorial on building a nib file from scratch (see "Your First iPhone Application") that covers Rules 1 and 2. However, it doesn't stress sufficiently that failing to observe these procedures is going to cause you grief.

—T.T.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video