TweetFollow Us on Twitter

Built for iPhone 2.0

Volume Number: 24
Issue Number: 12
Column Tag: iPhone

Built for iPhone 2.0

Developing utility applications with the iPhone SDK

by Rich Warren

Getting Started

So, you're interested in dipping your big toe into the fast-pace world of iPhone development? Well, what are you waiting for? Grab a copy of the SDK from http://developer.apple.com/iphone/. Install it, and you're good to go.

iPhone applications are, in many ways, much simpler than desktop applications. There are fewer options, fewer moving parts to juggle. It's a great way to learn the basics of Objective-C and Cocoa programming.

Even better: you don't even need to own an iPhone or use your personal one as a text platform. You can use the built-in simulator to give the SDK a test drive. Of course, if you want to actually distribute your applications, you need to apply to the iPhone Developer Program, and you will want to test your application on real hardware. Let's not worry about the details. For now, lets ease ourselves into the world of iPhone development one small step at a time.

Three Flavors of iApps

The first step is often the hardest. Before we can begin building our application, we must have some kind of design, at least a rough sketch on a beer-soaked bar napkin. We're not looking for a laundry list of features here. iPhone applications should focus on a single task or a tightly integrated set of tasks. People use them while waiting in line at the bank, or during their walk from the parking lot to the restaurant. They want to get in, get their information and get out.

Generally, this means our application should focus on the users. What sort of data are they interested in? How will they interact with it? How can we make their experience as streamlined and hassle-free as possible? To help answer these questions, Apple has identified three different basic styles for iPhone applications: Utility, Productivity and Immersive.

Utility Applications

Utility applications typically present information with little or no interaction. Many utility applications focus on a single, main view. They use a clean, simple interface to display their information as clearly as possible. More complex examples may include multiple views through either a tab bar or a page control.

Like Dashboard widgets, Utility applications have an info button on the bottom right corner. When you press this button, the application flips over, revealing a configuration panel. This lets users make quick, frequent changes to the application's behavior.

Weather and Stocks are perfect examples of Utility Applications.

Productivity Applications

Productivity applications manage complex, hierarchical data. Typically these applications are organized as a series of tables. Make a selection on one table, and a new list of options slide into place. The application starts with the most general information, with options growing more specific as you slide to the right. Productivity applications also make heavy use of navigation and tab bars. However, they use little or no custom drawing.

Productivity applications often have more-complex preferences, but the preferences do not change very frequently. Instead of showing the preferences within the application itself, the productivity application integrates its preferences into the iPhone's Settings application.

Mail is undoubtedly the stereotypical productivity application. The Settings app and, to a certain extent, the iPod app also fall into this category. Note: productivity applications are not necessarily limited to traditional productivity-oriented tasks, like ToDo lists and calendars. Any application that focuses on data and organization may benefit from the productivity style.

Immersive Applications

Immersive applications use the entire screen to provide an engaging view into the application's content. These applications always have a strong visual focus. They usually hide the status bar to maximize screen space. They use few if any common controls, favoring custom-drawn interfaces, and they are often designed to view horizontally.

Immersive applications include games, multi-media and graphics applications. The iPhone's video interface is a perfect example of an immersive application.

Above and Beyond

These three styles provide a solid foundation for building your iPhone application, but don't feel trapped or limited by them. They simply represent tested solutions to the most-common use cases. You will find many advantages to following these styles. Apple's developer tools provide excellent support for building these applications. Additionally, since many of Apple's own applications follow these styles, users already understand and feel comfortable with these designs.

Still, some of the original iPhone applications fail to remain squarely in any one category. When you first open the iPod app, it appears to follow the productivity style, focusing on the hierarchical organization of your media. However, the Cover Flow, Now Playing and Video views shift dramatically to the immersive.

Part of the fun of designing applications for the iPhone is simply experimenting with the interfaces and form factor. Don't forget to have fun! Use these styles as appropriate, but feel free to strike out on your own from time to time.

Tools of the Trade

Apple has done an excellent job designing the iPhone SDK so that developers can leverage their existing Cocoa knowledge. The tools and the APIs will feel very familiar, however, looks can be deceiving. In some important ways, iPhone applications are very different from their desktop cousins.

Let's take a quick look at the tools used for iPhone development, while paying special attention to any surprises that may be lurking in dark corners.

Xcode

If you've done any Cocoa development, you're already familiar with Xcode. If not, Xcode is Apple's IDE; your one-stop shop for all your code mangling and debugging needs. Don't worry, we'll step through the basics soon enough.


Xcode's iPhone Projects

Xcode 3.1 offers complete support for the iPhone SDK. This includes six different iPhone templates. The Utility Application template sets up a typical utility application. This includes the application's main view, the flipside view and the buttons to navigate between them. We'll take a detailed look at this template later on.

The Navigation-Based Application and the Tab Bar Application provide solid starting points for productivity projects. The Navigation-Based Application starts with a navigation bar and a table view. The Tab Bar Application gives you a tab bar and two views that you can toggle between.

Next, the OpenGL ES Application provides a view and animation timer. The template also includes a sample animation to get you started. Not surprisingly, the OpenGL ES Application template is an ideal starting point for any OpenGL-based immersive applications.

The last two templates, View-Based Application and Window-Based Application, provide simpler starting points for those who like to roll their own. The View-Based template provides a view controller and a single nib-based view. The Window-Based Application starts with even less.

We can use the View-Based and Window-Based templates to build any style of application. In particular, we can use them as starting points for immersive applications based on Quartz or Core Animation instead of OpenGL ES. Also, these applications provide ideal starting points for unusual applications that defy our regular classifications. We could even use them to build typical utility or productivity applications, but I would not recommend it. The other templates give you so much functionality for free. In general, you should always try to use the built in features first; save the custom code for when it's absolutely necessary.

Xcode also provides access to a vast library of information. Select Help --> Documentation to bring up the documentation window. To save time on the downloads, Xcode initially ships with only stubs to the core documents. To get the full libraries, you must subscribe to the appropriate documentation set.

In the documentation window, simply click the Get button next to the doc set you wish to install. I'd recommend subscribing to at least the Core Library, Developer Tools Library and iPhone OS Library. Be patient, these downloads can take a while. However, once they're installed, Xcode will automatically check for any updates every time you open the documentation window.


Xcode's Documentation

For more information on Xcode, check out Apple's Xcode Overview in Xcode's documentation.

Interface Builder

Interface builder lets us visually design our user interface. But it's more than that. We can also draw connections between the interface and our code, linking controls with actions or elements. The Interface Builder saves the layout and objects in either a *.nib or *.xib file. For the most part, the difference is unimportant. xib is a newer, develop-time format. It stores the nib as a flattened text file. This provides better integration between Interface Builder and Xcode, and makes xib files diff-able, more refactorable and SCM friendly. Finally, xibs are compiled into nib files when you build the application.

iPhone applications always use xib files. However, for historical reasons, both files types are commonly referred to as nibs. [Ed. note -- The term "nib" comes from NeXT. Remember them? NIB is an abbreviation for NeXT Interface Builder.]

Your application loads its nibs, creating instances of all the objects contained within.

Interface Builder becomes somewhat simpler when designing views for the iPhone. We don't have to worry about menus or bindings. Our window always fills the entire screen. The user cannot resize it or open multiple windows. This means we only need one window per application, and we can just swap in alternate views as necessary.

We should always keep our nib files as small as possible, but this is particularly true for the iPhone. Apple strongly recommends placing each view in its own nib. This allows us to load and unload the views individually. By default, view controllers respond to didReceiveMemoryWarning messages by purging any views that are not currently in use. Using one nib per view helps facilitate clean memory management.

Interface Builder has three main windows: the Document Window, the Library and the Inspector, plus any views or windows we are designing. The Document Window contains our top-level objects. Typically, this includes the File's Owner, First Responder, any additional controllers and any top-level views or windows. The Library contains a selection of controllers, views and controls that we can drag onto our user interface. Meanwhile, the Inspector allows us to view and edit the settings of any currently selected item.


Document Window


Library


Inspector

In general, Interface Builder development consists of dragging items from the library. Controllers and top-level windows or views are typically placed in the Document Window. Controls and subviews are placed directly on the desired window or view. We then resize our controls and views, and adjust their settings in the Inspector. Finally, we draw connections between the controllers in the document window and the controls on our user interface.

When building iPhone applications, any top-level window will have an arrow button in the upper right corner. This allows you to easily rotate between the horizontal and vertical orientations. However, if you're working on a View, you don't get a rotate button. That's a shame. Often your actual controls are located on the View, not the Window, and there is no easy way to check and see how your view will look when the iPhone is rotated.

Don't worry if this seems confusing. We'll get some hands on practice real soon, but if you would like more information on Interface Builder and nib files, check out Apple's Interface Builder User Guide in Xcode's documentation.

Instruments

Instruments allows us to examine the inner workings of running code. When working on iPhone projects, instruments allows us to collect data from either the simulator or the iPhone itself.

Instruments appears similar to GarageBand. You drag instruments from the library - each one monitors a specific type of data. You can modify each instrument's settings. Then select the desired executable and press the Record button. Instruments will gather data as your application runs, allowing you to explore it and look for problems.

You can also save the data, to explore it in more detail later, or to compare performance across different trials.


Instruments

Given the iPhone's limited resources, tuning an application with Instruments is a vital step in application development. At a minimum, you should look for and fix any memory leaks. You may also want to monitor CPU usage and look for performance bottlenecks. You should start exploring your application early in the development process. Leaving it to the end only makes problems harder to find and fix.

Finally, while you can use Instruments with the simulator for some early testing, the simulator does not accurately represent your application's performance on the iPhone itself. You should always do your final testing on the actual iPhone hardware.

For more information on Instruments, check out Apple's Instruments User Guide in the Xcode documentation.

Objective-C and Cocoa Touch

These may not be actual tools - they're more like the raw materials that we use to shape our applications. Semantics aside, there are several important issues we should consider before moving forward.

The iPhone SDK uses a subset of Objective-C 2.0. This includes many powerful technologies: properties, fast enumeration, and key-value observing. However, it does not support garbage collection. Not yet, at least.

That means, before you can develop iPhone applications, you must understand memory management in Objective-C. It's not hard, especially when we use properties, but it does require a bit of care and attention. Basically, you must release anything you create or anything you retain. We'll look at some examples in the tutorial below, but to really understand the topic, check out Apple's Memory Management Programmer's Guide for Cocoa in Xcode's documentation.

If you're an experienced Cocoa programmer, you'll quickly notice that many of the AppKit NS* classes have been replaced by new UI* classes from Cocoa Touch's UIKit. Many of these classes have similar names and play similar roles, but there are some subtle (and sometimes not-so-subtle) differences. When in doubt, check the documentation.

Also, even though Cocoa Touch includes most of Cocoa's features, it doesn't duplicate everything. The biggest exceptions, in my opinion, are the lack of Bindings and Core Data. Maybe I'm just lazy, but I've grown used to letting Bindings and Core Data do much of the glue work for me. Having to go back and make all these connections by hand feels like I've fallen into the Stone Age. Maybe Bindings and Core Data are just not efficient enough for an embedded device? Maybe they're features Apple will add in the future? Regardless, they're not here now, and that's a shame.

The iPhone can also be rather draconic when it comes to shutting down the application or managing memory. As you've probably heard, the iPhone does not support running third-party applications in the background. More importantly, our active application will shut down whenever the user presses the home button, or whenever the phone receives an incoming call. This means, our application could shut down at any point in time - whether or not the user wants it to.

Just before the application shuts down, the iPhone OS sends our application delegate an applicationWillTerminate: message. This allows us to perform any necessary cleanup and save off our state before closing. However, our application must respond quickly. If it does not exit on its own within 5 seconds, the OS will kill the process outright.

Also, due to the iPhone's limited resources, there are a number of memory warnings that we must listen for. The iPhone OS's virtual memory does not include any swap space. When memory begins to run low, the OS will send a message to the running application. The application delegate receives an applicationDidRecieveMemoryWarning: message. We can override this method to free up memory.

Additionally, each UIViewController subclass will also receive a didRecieveMemoryWarning message. By default, our application will release any views that are not currently being used. It will then recreate the views later, if they are needed again. However, we can override didRecieveMemoryWarning to change this behavior, or to perform additional memory management at the controller level.

Finally, our application can register to receive UIApplicationdidRecieveMemoryWarningNotification messages. This can be particularly useful when we have more than one object that manages our purgeable resources - particularly if we do not need to coordinate between these objects. If we want a more coordinated purge, we probably need to organize it at the application level, through the application delegate's didRecieveMemoryWarning method.

It is vitally important to clean up as much memory as quickly as possible. If the memory shortage persists, the iPhone OS will shut down the entire application.

For more information on development for the iPhone, check out the iPhone OS Programming Guide, in Xcode's documentation.

Color World Tutorial

For the rest of this article, we will explore the iPhone SDK by building a simple utility application. Color World displays and hides the message "Hello, World!" when the user presses its button. If you go to the FlipsideView, you can set the color for both the text and the main View's background.


Main View


Flipside View

Build the Project

Open Xcode and select File --> New Project.... In the New Project window, make sure iPhone OS application is selected in the left-hand table. Now double click the Utility Application icon. A Save sheet should drop down. Type "Colorful World" for the project's name, and set an appropriate location.

Xcode now opens your new project. Let's take a second to get ourselves oriented. In the Groups & Files tree, you should see several folders under the Colorful World project. We will primarily use files from the Main View, Flipside View, Application Controllers and Resources folders. Main View contains the header and implementation files for our MainView and MainViewController. Flipside View holds similar files for our flipside view. Application Controllers manages the Colorful_WorldAppDelegate and our RootViewController. While the Resources folder holds all our nibs: FlipsideView.xib, MainView.xib and MainWindow.xib.


Groups & Files

Go ahead and press the Build and Go button from Xcode's toolbar. This will automatically launch our application in the simulator. As you can see, it doesn't do much yet. The main view is just a gray background, with the info button in the bottom right corner. However, if you click on the info button, it automatically flips to the FlipsideView, which is dark grey with a black navigation bar and a nice blue Done button. As you can see, we get quite a bit of functionality for free.

Build our Model

iPhone applications typically follow a Model-View-Controller (MVC) design pattern. The MVC paradigm divides a complex application into logical chunks. The Model manages the application's data. The view displays the data. And the controller handles user interactions. Xcode has already built stubs for our views and controllers. All we need now is a model.

Create a new group by right clicking on Colorful World in the Groups & Files tree. Select Add --> New Group. Then rename the group Model. Now right click on our Model group and select Add --> New File.... In the New File window, make sure iPhone OS Cocoa Touch Classes is selected. Double click NSObject subclass. In the New File window, type Model.m for the file name. Make sure also create "Model.h" is checked. Then click Finish.

Our model is incredibly simple. Open the interface file, Model.h. Make the changes shown below:

Model.h

This is the interface for Colorful World's Model.

@interface Model : NSObject {
   
   UIColor *viewColor;
   UIColor *labelColor;
}
@property(readwrite, retain) UIColor *viewColor;
@property(readwrite, retain) UIColor *labelColor;
@end

This simply creates two UIColor member variables, and declares properties to provide read/write accessors for these variables. Technically, all properties are read/write by default, so the property lines could be simplified to @property(retain) .... However, when building more-complex objects I often find it helpful to explicitly define my properties.

viewColor defines the background color for our main view. labelColor defines the message text color (when visible).

Now, open the implementation file (Model.m). Again, make the changes listed below.

Model.m

The implementation for Colorful World's Model.

@implementation Model
@synthesize viewColor, labelColor;
- (id)init {
   
   if (self = [super init]) {
      
      self.viewColor = [UIColor lightGrayColor];
      self.labelColor = [UIColor whiteColor];
   }
   return self;
   
}
- (void)dealloc {
   
   [viewColor release];
   [labelColor release];
   [super dealloc];
}
@end

This is almost as simple as the interface. @synthesize creates the accessors for our properties. init sets our default colors. Note: since we use our properties to store the colors, we do not need to worry about retaining them. Our accessors handle that automatically. However, we do need to release our colors when the model is disposed. Not surprising, that is done in the dealloc method.

That's it. Our model is done.

Add the Model

Both the main view and the flipside view need to access our model. There are several ways we could manage this. Probably the simplest is to just add our model to our application delegate. Our view controllers can then access our model through the delegate.

First, open Colorful_WorldAppDelegate.h and make the changes shown below:

Colorful_WorldAppDelegate.h

We add a model property to our application delegate.

#import <UIKit/UIKit.h>
@class RootViewController;
@class Model;
@interface Colorful_WorldAppDelegate : NSObject <UIApplicationDelegate> {
   IBOutlet UIWindow *window;
   IBOutlet RootViewController *rootViewController;
   Model *model;
}
@property (nonatomic, retain) UIWindow *window;
@property (nonatomic, retain) RootViewController *rootViewController;
@property (nonatomic, readonly) Model * model;
@end

We're basically adding a model property to our delegate. To add the model, we must first make a class declaration for the Model object. Then we add a model instance variable. Finally, we declare that model is a property. Specifically, we say that the model is an unsynchronized, read-only property.

Now we need to synthesize and initialize our model. Open Colorful_WorldAppDelegate.m, and add the following code just below the other @synthesize lines.

[Colorful_WorldAppDelegate init]

Synthesize our model, and then initialize it in the delegate's default constructor.

@synthesize model;
-(id)init {
   if (self = [super init]) {
      model = [[Model alloc] init];
   }
   
   return self;
}

The init method creates a new instance of Model and assigns it to our model property. If you've been paying attention, you would have noticed a similar design used in Model's constructor. It may look a bit intimidating, but it's a common idiom for Objective-C. In theory, the call to [super init] could return an entirely different object, or nil, and our constructor may need to respond accordingly.

Finally, we need to import the header for our Model class. Add the following line just below the other imports.

#import "Model.h"

Build Main View

The RootViewController manages our main window, and handles transitions between the main view and the flipside view. The real work is done by the main view and the flipside view themselves. Actually, that's not true. Typically you won't ever touch the views, except in Interface Builder. Code-wise, the view controllers do all the work. They are responsible for loading and unloading new views, swapping views, managing auto-rotation, and responding to user-generated events. Let's take a look.

Open MainViewController.h, and change it to match the code below:

MainViewController.h

This view displays our message and a single button that turns the message on and off.

#import <UIKit/UIKit.h>
@interface MainViewController : UIViewController {
   IBOutlet UIButton *button;
   IBOutlet UILabel *label;
}
- (IBAction) buttonPressed;
@end

So, what have we done here? The two IBOutlet lines define pointers to objects in our main view's nib: one for the button and one for the label. Finally, we declare an action, buttonPressed, which (not surprisingly) is called whenever our button is pushed.

Just a quick note on IBOutlet and IBAction, IBOutlet has no real meaning outside Interface Builder. As far as our code is concerned, button and label are simply instance variables. The IBOutlet macro just alerts Interface Builder to the outlet's existence. This will allow us to draw connections using Interface Builder later on.

Similarly, but even more simply, IBAction equals void. Actions are just methods that don't take any arguments and don't return any values. Again, like IBOutlet, IBAction is a flag for Interface Builder, allowing us to draw the necessary connections later on.

OK, now open the implementation file (MainViewController.m).

First, lets look at an unmodified view controller. Ours has four methods and a commented-out stub. initWithNibName:bundle: is called to initialize MainViewController instances. Unfortunately, the nib has not been loaded at this point, so any outlets will not be valid. While you can do some initialization here, you cannot touch any objects that are loaded through the nib. Typically I wait and do all my initialization in viewDidLoad. Once here, all the nib-loaded objects are guaranteed to be valid.

Next, we have shouldAutorotateTo-InterfaceOrientation. iPhone OS calls this method whenever the iPhone's orientation changes. If you want to allow autorotation, you can simply return YES.

The OS also calls didRecieveMemoryWarning whenever the phone is running low on memory. By default, the OS will release any view controllers that are not currently in use. You can perform additional memory management here as well. Our application does not have any purgeable memory, so we will just leave the default implementation alone.

Finally, the runtime calls dealloc on an instance just before that instance is destroyed. Typically we release any references that we are holding, before calling the superclass's dealloc method.

Let's start by adding our initialization. Uncomment viewDidLoad and add the following code:

[MainViewController viewDidLoad]

Additional initialization for MainViewController.

- (void)viewDidLoad {
   id delegate = [[UIApplication sharedApplication] delegate];
   Model *model = (Model *)[delegate model];
   
   [model addObserver:self forKeyPath:@"viewColor" 
      options: NSKeyValueObservingOptionNew context:nil];
   
   [model addObserver:self forKeyPath:@"labelColor" 
      options: NSKeyValueObservingOptionNew context:nil];
   
   self.view.backgroundColor = model.viewColor;
   label.textColor = model.labelColor; 
 }

First, we get a copy of our model from the singleton UIApplication instance. Then, we use key-value observing to monitor our model. We register our MainViewController instance as an observer of both the viewColor and the labelColor properties. Anytime either of these properties change, we will receive a notification message.

Key-value observing is built into all NSObject subclasses. You can observe any value from any object, as long as that value is key-value compliant. Usually, this simply requires key-value compliant accessors for the member variable. By definition, properties autogenerate key-value compliant accessors, which is just another reason to use properties.

For more information about key-value observing, check out the Key-Value Observing Programming Guide in Xcode's documentation.

Once we're done registering with the model, we simply set our default colors. Notice that neither of these settings could have been made during the initWithNibName:bundle: method. The view and label values are both initialized when the nib loads.

Now, we need to catch the notifications from our model. Add the following method.

[MainViewController observeValueForKeyPath:ofObject:change:context:]

Catches any key-value observing messages to this object.

- (void)observeValueForKeyPath:(NSString *)keyPath
      ofObject:(id)object 
      change:(NSDictionary *)change
      context:(void *)context
{
   
   id newValue = [change objectForKey:NSKeyValueChangeNewKey];
   
   if ([keyPath isEqual:@"viewColor"]) {
      self.view.backgroundColor = newValue;
   }
   
   if ([keyPath isEqual:@"labelColor"]) {
      label.textColor = newValue;
   }   
}

This method will catch any notifications from any objects we are observing. We simply get the value of the change, then check to see which variable has changed. If the viewColor changed, we set the new background color. If the labelColor changed, we set the new text color.

Next, we need to build our button action. Just add the following method:

[MainViewController buttonPressed]

This method is called whenever the user presses the Display Message button.

- (IBAction)buttonPressed {
   
   Boolean hidden = label.hidden;
   
   if (hidden) {
      [button setTitle:@"Hide Message" forState:UIControlStateNormal];
   }
   else {
      [button setTitle:@"Display Message"   forState:UIControlStateNormal];
   }
   
   label.hidden = !hidden;
}

Here, we check to see if our label is visible. If it is visible, we set the button's text to "Display Message" and hide the label. If it's hidden, we set the button's text to "Hide Message" and make the label visible. Pretty simple, really.

Import the Model's header, and you're good to go.

Build Flipside View

Simple UI controls use IBOutlets and IBActions to communicate with their controller. More complex controls use delegates. Our flipside view will use a UIPickerView to select the background and text colors. This view will need a UIPickerViewDelegate subclass to communicate with.

Right click on the Flipside View group, and select Add

--> New File.... Add a new NSObject subclass and name it ColorPickerViewDelegate. Open the header file, and make the following changes:

ColorPickerViewDelegate.h

Interface for our UIPickerView delegate.

#import <UIKit/UIKit.h>
@class Model;
@interface ColorPickerViewDelegate : NSObject 
   <UIPickerViewDelegate, UIPickerViewDataSource> {
   
   NSArray *titles;
   NSArray *colors;
   Model * model;
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView;
- (NSInteger)pickerView:(UIPickerView *)pickerView 
   numberOfRowsInComponent:(NSInteger)component;
- (NSString *)pickerView:(UIPickerView *)pickerView 
   titleForRow:(NSInteger)row forComponent:(NSInteger)component;
@end

Our delegate contains an array of colors, an array of color titles and a pointer to our model. We implement two required methods from the UIPickerViewDataSource protocol (numberOfComponentsInPickerview: and pickerView:numberOfRowsInComponent:). We also implement two optional methods from the UIPickerViewDelegate protocol (pickerView:titleForRow:forComponent: and pickerView:didSelectRow:inComponent:). These methods allow us to create the row labels, and respond to any user selections.

Now open the implementation file. Again, make the following changes:

ColorPickerViewDelegate.m

The implementation of our UIPickerViewDelegate.
#import "ColorPickerViewDelegate.h"
#import "Model.h"
@implementation ColorPickerViewDelegate
-(id)init{
   if (self = [super init]) { 
      
      titles = [NSArray arrayWithObjects:  @"Red", @"Blue", @"Green", 
         @"Gray", @"Light Gray", @"White", nil];
      
      [titles retain];
      
      colors = [NSArray arrayWithObjects: [UIColor redColor], 
         [UIColor blueColor], [UIColor greenColor],[UIColor grayColor],
         [UIColor lightGrayColor], [UIColor whiteColor],  nil];
      
      [colors retain];
      
      id delegate = [[UIApplication sharedApplication] delegate];
      model = (Model *)[delegate model];
      [model retain];
   }
   
   return self;
}
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
   return 2;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
   return [colors count];
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
   return [titles objectAtIndex: row];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
   
   UIColor * newColor = [colors objectAtIndex:row];
   
   if (component == 0) {
      model.viewColor = newColor;
   }
   
   else {
      model.labelColor = newColor;
   }
}
- (void)dealloc {
   [titles release];
   [colors release];
   [model release];
   [super dealloc];
}
@end

There's a lot going on here, so let's take it slowly. First, we declare an array of titles and an array of colors. The titles contain the names of each color, and are used to create our labels. The colors, on the other hand, will be sent to the model whenever the user makes a new selection.

The numberOfComponentsInPickerView: method simply returns the number of components we want for our view. For example, a four-digit combination lock view would have four components. Ours only has two: one for the background color, another for the text color.

The pickerView:numberOfRowsInComponent: method sets the number of rows in each component. A more complex view, like a date view, may have a different number of rows for each component. We use the same set of colors for both. Therefore, we just return the number of colors in our array.

The picker view then iterates over the rows in each component, calling pickerView:titleForRow:forComponent: on each one. This method returns the NSString title used to label that row.

Finally the picker calls pickerView:didSelectRow-:inComponent: whenever the user makes a selection. Here, we check to see which component the user altered, then update our model as appropriate.

Now, we need to attach our delegate to our picker view. We have two basic options, we can pull the picker view from the nib, or we can push our delegate into the nib. Either method works fine. I chose the first option, since it is slightly simpler.

Open FlipsideViewController.h. We need to add an IBOutlet for our picker view, and we need an instance variable for our delegate.

FlipsideViewController.h

The interface for our flipside view controller.

#import <UIKit/UIKit.h>
@class ColorPickerViewDelegate;
@interface FlipsideViewController : UIViewController {
   IBOutlet UIPickerView *colorPickerView;
   ColorPickerViewDelegate *delegate;
}
@end

Next, open the implementation file and implement viewDidLoad, as shown below:

[FlipsideViewController viewDidLoad]

Instantiates our delegate, then assigns it to the picker view.

- (void)viewDidLoad {
   self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];   
   
   delegate = [[ColorPickerViewDelegate alloc] init];
   colorPickerView.delegate = delegate;
}

Don't forget to import the ColorPickerViewDelegate header file, and don't forget to release our delegate in the dealloc method. But, other than that, we're done here.

Create the User Interface

We just need to build our views, and our application is done. Let's start with the main view. Within the Resources group, double click on MainView.xib. This will launch Interface Builder.

Drag a label from the library and center it on the view. Double click the label and type "Hello, World!" Its text value should change. Now, with the label selected, take a look at the inspector.

The first tab lets us set our attributes. We only need to make one change here. Our message should start out hidden. In the View, Drawing attributes, make sure Hidden is checked.

The second tab shows us our connections. We don't have any yet. Just move on.

The third tab allows us to change the size, fix the alignment and set the autoresizing. Of these, autoresizing is the most interesting. It lets us define how a UI element will behave when the window's size changes.

That may seem a bit odd for an iPhone application. After all, the user cannot resize the window, it always fills the screen. However, if you want your application to autorotate (or at least, if you want it to look halfway decent after you autorotate), you need to define appropriate autoresize settings.

Our application does not autorotate, so we can ignore those settings for now. Just make sure your label is centered horizontally. You probably want it somewhat above the vertical center.

The fourth tab shows us the selected object's class and any actions our outlets for that class. Again, we have none.

Drag a rounded rectangle button from the library and place it along the bottom of the view. As you move it around, you should see blue lines whenever you approach an edge, or when you are centered under the label. Use these to position the button, then double click the button. Type "Display Message". Again, we don't need to change any of the button's settings.

Now lets connect these items to our File's Owner. Right click on the File's Owner in the Documents window. This brings up a semi-transparent, dark-gray list of all the actions and outlets for the File's Owner. Move this list so that you can see both it and the view.

Find the button outlet. It should have an empty circle at the right edge of the row. When you hover your mouse over the circle, it becomes a plus sign. Click and drag from that circle to the button control on your view. Our File's Owner list should now show our connection. Now, click and drag from the label outlet to the label control.

Connecting the action is almost as easy. Drag from the button pressed action to the button control. This time, you will see a popup menu of options. Select Touch Up Inside. That's it. We're done with the main view. Save it and close it.


MainView.xib Connections


Main View

Now open FlipsideView.xib. Drag out a picker view. Center this at the top of the flipside view. Drag out two labels. Put one beneath the picker on the left side, and change the text to "Background". Put the other on the right and change the text to "Text". You may want to change the text color for these to something lighter, just to make them a little easier to see. You should also right align the "Text" label.

Again, right click on the File's Owner. Drag from the colorPickView outlet to the picker view control. That's it. You're done. Save and exit.


Flipside Connections


Flipside View

Take it for a Spin

Now's the moment of truth. Press the build and go button, cross your fingers and wait. If everything went well, the simulator should launch, and you should have a fully functional (if somewhat silly) application.

What do you do if things don't go as planned? Take a quick look through the errors and see if you can figure out what went wrong. Often, it's only worth looking at the first few error messages. A single mistake could spawn a cascade of rather strange errors. Try to fix the first thing on the list, then rebuild the application.

You can also look for common mistakes. Make sure there's a semicolon at the end of the line. Parenthesis and brackets always come in pairs. When you have nested functions, it's often easy to get off by one. Also, make sure you've imported header files wherever we've used custom objects. Finally, double-check the connections in Interface Builder.

A little patience and persistence, and the application will be up and running in no time. However, you're still not done. You should really launch the application in Instruments and check for memory leaks or performance bottlenecks. This application's so small, you shouldn't find much, but it's still an interesting exercise. For example, you will see sudden jump in memory usage once the flipside view is loaded.

Room for Improvement

Our program, as written, has two obvious bugs. First, when we switch to the flipside view, our picker should have the current colors selected. Second, we should not let the user select the same color for both the background and the text. I'll leave those as a homework assignment for you.

And for extra credit, try this. Our application should really save our model when the user presses the Done button on the flipside view. We should also load the model when the application starts. This way, our application will remember the users most recent color selection.

This is harder than the simple bug fixes above (hence the extra credit). But, you should be able to get through it. Take a look at the RootViewController's toggleView method, and read the section titled The Application Sandbox from the iPhone OS Programming Guide.

Good luck, and happy coding!


Rich Warren lives in Honolulu, Hawaii with his wife, Mika, daughter, Haruko, and his son, Kai. He is a software engineer, freelance writer and part time graduate student. When not playing on the beach, he is probably writing, coding or doing research on his MacBook Pro. You can reach Rich at rikiwarren@mac.com, check out his blog at http://freelancemadscience.blogspot.com/ or follow him at http://twitter.com/rikiwarren.

 
AAPL
$97.36
Apple Inc.
+0.33
MSFT
$44.51
Microsoft Corpora
+0.11
GOOG
$587.84
Google Inc.
-5.51

MacTech Search:
Community Search:

Software Updates via MacUpdate

TinkerTool 5.3 - Expanded preference set...
TinkerTool is an application that gives you access to additional preference settings Apple has built into Mac OS X. This allows to activate hidden features in the operating system and in some of the... Read more
Audio Hijack Pro 2.11.0 - Record and enh...
Audio Hijack Pro drastically changes the way you use audio on your computer, giving you the freedom to listen to audio when you want and how you want. Record and enhance any audio with Audio Hijack... Read more
Intermission 1.1.1 - Pause and rewind li...
Intermission allows you to pause and rewind live audio from any application on your Mac. Intermission will buffer up to 3 hours of audio, allowing users to skip through any assortment of audio... Read more
Autopano Giga 3.6 - Stitch multiple imag...
Autopano Giga allows you to stitch 2, 20, or 2,000 images. Version 3.0 integrates impressive new features that will definitely make you adopt Autopano Pro or Autopano Giga: Choose between 9... Read more
Airfoil 4.8.7 - Send audio from any app...
Airfoil allows you to send any audio to AirPort Express units, Apple TVs, and even other Macs and PCs, all in sync! It's your audio - everywhere. With Airfoil you can take audio from any... Read more
Microsoft Remote Desktop 8.0.8 - Connect...
With Microsoft Remote Desktop, you can connect to a remote PC and your work resources from almost anywhere. Experience the power of Windows with RemoteFX in a Remote Desktop client designed to help... Read more
xACT 2.30 - Audio compression toolkit. (...
xACT stands for X Aaudio Compression Toolkit, an application that encodes and decodes FLAC, SHN, Monkey’s Audio, TTA, Wavpack, and Apple Lossless files. It also can encode these formats to MP3, AAC... Read more
Firefox 31.0 - Fast, safe Web browser. (...
Firefox for Mac offers a fast, safe Web browsing experience. Browse quickly, securely, and effortlessly. With its industry-leading features, Firefox is the choice of Web development professionals... Read more
Little Snitch 3.3.3 - Alerts you to outg...
Little Snitch gives you control over your private outgoing data. Track background activityAs soon as your computer connects to the Internet, applications often have permission to send any... Read more
Thunderbird 31.0 - Email client from Moz...
As of July 2012, Thunderbird has transitioned to a new governance model, with new features being developed by the broader free software and open source community, and security fixes and improvements... Read more

Latest Forum Discussions

See All

New Trailer For Outcast Odyssey, A New K...
New Trailer For Outcast Odyssey, A New Kind of Card Battler Posted by Jennifer Allen on July 25th, 2014 [ permalink ] Out this Fall is a new kind of card battle game: Outcast Odyssey. | Read more »
Garfield: Survival of the Fattest Coming...
Garfield: Survival of the Fattest Coming to iOS this Fall Posted by Jennifer Allen on July 25th, 2014 [ permalink ] Who loves lasagna? Me. Also everyone’s favorite grumpy fat cat, Garfield. | Read more »
Happy Flock Review
Happy Flock Review By Andrew Fisher on July 25th, 2014 Our Rating: :: HERD IT ALL BEFOREUniversal App - Designed for iPhone and iPad Underneath the gloss of Happy Flock’s visuals is a game of very little substance. It’s cute, but... | Read more »
Square Register Updates Adds Offline Pay...
Square Register Updates Adds Offline Payments Posted by Ellis Spice on July 25th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Looking For Group – Hearthstone’s Curse...
For the first time since its release (which has thankfully been a much shorter window for iPad players than their PC counterparts), Blizzard’s wildly successful Hearthstone: Heroes of Warcraft CCG is sporting some brand new content: the single... | Read more »
Poptile Review
Poptile Review By Jennifer Allen on July 25th, 2014 Our Rating: :: SIMPLY FUNUniversal App - Designed for iPhone and iPad Simple yet a little bit glorious, Poptile is a satisfying entertaining puzzle game with oodles of the ‘one... | Read more »
Modern Combat 5: Blackout Review
Modern Combat 5: Blackout Review By Brittany Vincent on July 25th, 2014 Our Rating: :: LESS QQ, MORE PEW PEWUniversal App - Designed for iPhone and iPad The fifth entry into the blockbuster Modern Combat series is what mobile... | Read more »
Watch and Share Mobile Gameplay Videos W...
Watch and Share Mobile Gameplay Videos With Kamcord Posted by Jennifer Allen on July 25th, 2014 [ permalink ] iPhone App - Designed for the iPhone, compatible with the iPad | Read more »
THE KING OF FIGHTERS '98 (Games)
THE KING OF FIGHTERS '98 1.0 Device: iOS Universal Category: Games Price: $3.99, Version: 1.0 (iTunes) Description: Series’ masterpiece “KOF ’98” finally joins the battle on iPhone! FEATURES:■ The best game balance in the “KOF”... | Read more »
LEX Goes Free For One Day In Honor of Ne...
LEX Goes Free For One Day In Honor of New Update Posted by Jennifer Allen on July 24th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »

Price Scanner via MacPrices.net

iMacs on sale for $150 off MSRP, $250 off for...
Best Buy has iMacs on sale for up to $160 off MSRP for a limited time. Choose free home shipping or free instant local store pickup (if available). Prices are valid for online orders only, in-store... Read more
Mac minis on sale for $100 off MSRP, starting...
Best Buy has Mac minis on sale for $100 off MSRP. Choose free shipping or free instant local store pickup. Prices are for online orders only, in-store prices may vary: 2.5GHz Mac mini: $499.99 2.3GHz... Read more
Global Tablet Market Grows 11% in Q2/14 Notwi...
Worldwide tablet sales grew 11.0 percent year over year in the second quarter of 2014, with shipments reaching 49.3 million units according to preliminary data from the International Data Corporation... Read more
New iPhone 6 Models to Have Staggered Release...
Digitimes’ Cage Chao and Steve Shen report that according to unnamed sources in Apple’s upstream iPhone supply chain, the new 5.5-inch iPhone will be released several months later than the new 4.7-... Read more
New iOS App Helps People Feel Good About thei...
Mobile shoppers looking for big savings at their favorite stores can turn to the Goodshop app, a new iOS app with the latest coupons and deals at more than 5,000 online stores. In addition to being a... Read more
Save on 5th generation refurbished iPod touch...
The Apple Store has Apple Certified Refurbished 5th generation iPod touches available starting at $149. Apple’s one-year warranty is included with each model, and shipping is free. Many, but not all... Read more
What Should Apple’s Next MacBook Priority Be;...
Stabley Times’ Phil Moore says that after expanding its iMac lineup with a new low end model, Apple’s next Mac hardware decision will be how it wants to approach expanding its MacBook lineup as well... Read more
ArtRage For iPhone Painting App Free During C...
ArtRage for iPhone is currently being offered for free (regularly $1.99) during Comic-Con San Diego #SDCC, July 24-27, in celebration of the upcoming ArtRage 4.5 and other 64-bit versions of the... Read more
With The Apple/IBM Alliance, Is The iPad Now...
Almost since the iPad was rolled out in 2010, and especially after Apple made a 128 GB storage configuration available in 2012, there’s been debate over whether the iPad is a serious tool for... Read more
MacBook Airs on sale starting at $799, free s...
B&H Photo has the new 2014 MacBook Airs on sale for up to $100 off MSRP for a limited time. Shipping is free, and B&H charges NY sales tax only. They also include free copies of Parallels... Read more

Jobs Board

*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Sr. Project Manager for *Apple* Campus 2 -...
…the design and construction of one building or building components of the New Apple Campus located in Cupertino, CA. They will provide project management oversight for Read more
WW Sales Program Manager, *Apple* Online St...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.