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.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Whitethorn Games combines two completely...
If you have ever gone fishing then you know that it is a lesson in patience, sitting around waiting for a bite that may never come. Well, that's because you have been doing it wrong, since as Whitehorn Games now demonstrates in new release Skate... | Read more »
Call of Duty Warzone is a Waiting Simula...
It's always fun when a splashy multiplayer game comes to mobile because they are few and far between, so I was excited to see the notification about Call of Duty: Warzone Mobile (finally) launching last week and wanted to try it out. As someone who... | Read more »
Albion Online introduces some massive ne...
Sandbox Interactive has announced an upcoming update to its flagship MMORPG Albion Online, containing massive updates to its existing guild Vs guild systems. Someone clearly rewatched the Helms Deep battle in Lord of the Rings and spent the next... | Read more »
Chucklefish announces launch date of the...
Chucklefish, the indie London-based team we probably all know from developing Terraria or their stint publishing Stardew Valley, has revealed the mobile release date for roguelike deck-builder Wildfrost. Developed by Gaziter and Deadpan Games, the... | Read more »
Netmarble opens pre-registration for act...
It has been close to three years since Netmarble announced they would be adapting the smash series Solo Leveling into a video game, and at last, they have announced the opening of pre-orders for Solo Leveling: Arise. [Read more] | Read more »
PUBG Mobile celebrates sixth anniversary...
For the past six years, PUBG Mobile has been one of the most popular shooters you can play in the palm of your hand, and Krafton is celebrating this milestone and many years of ups by teaming up with hit music man JVKE to create a special song for... | Read more »
ASTRA: Knights of Veda refuse to pump th...
In perhaps the most recent example of being incredibly eager, ASTRA: Knights of Veda has dropped its second collaboration with South Korean boyband Seventeen, named so as it consists of exactly thirteen members and a video collaboration with Lee... | Read more »
Collect all your cats and caterpillars a...
If you are growing tired of trying to build a town with your phone by using it as a tiny, ineffectual shover then fear no longer, as Independent Arts Software has announced the upcoming release of Construction Simulator 4, from the critically... | Read more »
Backbone complete its lineup of 2nd Gene...
With all the ports of big AAA games that have been coming to mobile, it is becoming more convenient than ever to own a good controller, and to help with this Backbone has announced the completion of their 2nd generation product lineup with their... | Read more »
Zenless Zone Zero opens entries for its...
miHoYo, aka HoYoverse, has become such a big name in mobile gaming that it's hard to believe that arguably their flagship title, Genshin Impact, is only three and a half years old. Now, they continue the road to the next title in their world, with... | Read more »

Price Scanner via MacPrices.net

Deal Alert! B&H Photo has Apple’s 14-inch...
B&H Photo has new Gray and Black 14″ M3, M3 Pro, and M3 Max MacBook Pros on sale for $200-$300 off MSRP, starting at only $1399. B&H offers free 1-2 day delivery to most US addresses: – 14″ 8... Read more
Department Of Justice Sets Sights On Apple In...
NEWS – The ball has finally dropped on the big Apple. The ball (metaphorically speaking) — an antitrust lawsuit filed in the U.S. on March 21 by the Department of Justice (DOJ) — came down following... Read more
New 13-inch M3 MacBook Air on sale for $999,...
Amazon has Apple’s new 13″ M3 MacBook Air on sale for $100 off MSRP for the first time, now just $999 shipped. Shipping is free: – 13″ MacBook Air (8GB RAM/256GB SSD/Space Gray): $999 $100 off MSRP... Read more
Amazon has Apple’s 9th-generation WiFi iPads...
Amazon has Apple’s 9th generation 10.2″ WiFi iPads on sale for $80-$100 off MSRP, starting only $249. Their prices are the lowest available for new iPads anywhere: – 10″ 64GB WiFi iPad (Space Gray or... Read more
Discounted 14-inch M3 MacBook Pros with 16GB...
Apple retailer Expercom has 14″ MacBook Pros with M3 CPUs and 16GB of standard memory discounted by up to $120 off Apple’s MSRP: – 14″ M3 MacBook Pro (16GB RAM/256GB SSD): $1691.06 $108 off MSRP – 14... Read more
Clearance 15-inch M2 MacBook Airs on sale for...
B&H Photo has Apple’s 15″ MacBook Airs with M2 CPUs (8GB RAM/256GB SSD) in stock today and on clearance sale for $999 in all four colors. Free 1-2 delivery is available to most US addresses.... Read more
Clearance 13-inch M1 MacBook Airs drop to onl...
B&H has Apple’s base 13″ M1 MacBook Air (Space Gray, Silver, & Gold) in stock and on clearance sale today for $300 off MSRP, only $699. Free 1-2 day shipping is available to most addresses in... Read more
New promo at Visible: Buy a new iPhone, get $...
Switch to Visible, and buy a new iPhone, and Visible will take $10 off their monthly Visible+ service for 24 months. Visible+ is normally $45 per month. With this promotion, the cost of Visible+ is... Read more
B&H has Apple’s 13-inch M2 MacBook Airs o...
B&H Photo has 13″ MacBook Airs with M2 CPUs and 256GB of storage in stock and on sale for $100 off Apple’s new MSRP, only $899. Free 1-2 day delivery is available to most US addresses. Their... Read more
Take advantage of Apple’s steep discounts on...
Apple has a full line of 16″ M3 Pro and M3 Max MacBook Pros available, Certified Refurbished, starting at $2119 and ranging up to $600 off MSRP. Each model features a new outer case, shipping is free... Read more

Jobs Board

Medical Assistant - Surgical Oncology- *Apple...
Medical Assistant - Surgical Oncology- Apple Hill Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Business Analyst | *Apple* Pay - Banco Popu...
Business Analyst | Apple PayApply now " Apply now + Apply Now + Start applying with LinkedIn Start + Please wait Date:Mar 19, 2024 Location: San Juan-Cupey, PR Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.