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

FileMaker Pro 19.4.2 - Quickly build cus...
FileMaker Pro is the tool you use to create a custom app. You also use FileMaker Pro to access your app on a computer. Start by importing data from a spreadsheet or using a built-in Starter app to... Read more
Adobe Illustrator 26.0.3 - Professional...
You can download Adobe Illustrator for Mac as a part of Creative Cloud for only $20.99/month. Adobe Illustrator for Mac is the vector graphics classics in the design industry. It is a digital... Read more
WhatRoute 2.4.9 - Geographically trace o...
WhatRoute is designed to find the names of all the routers an IP packet passes through on its way from your Mac to a destination host. It also measures the round-trip time from your Mac to the router... Read more
Notion 2.0.20 - A unified workspace for...
Notion is the unified workspace for modern teams. Notion Features: Integration with Slack Documents Wikis Tasks Release notes were unavailable when this listing was updated. Download Now]]> Read more
Monterey Cache Cleaner 17.0.2 - Clear ca...
Monterey Cache Cleaner is an award-winning general-purpose tool for macOS X. MCC makes system maintenance simple with an easy point-and-click interface to many macOS X functions. Novice and expert... Read more
Firetask Pro 4.6.8 - Innovative task man...
Firetask Pro represents the next generation of easy-to-use, project-oriented task management apps. By combining David Allen's powerful Getting Things Done (GTD®) approach with classical task... Read more
Smultron 13.0.4 - Easy-to-use, powerful...
Smultron 13 is the text editor for all of us. Smultron is powerful and confident without being complicated. Its elegance and simplicity helps everyone being creative and to write and edit all sorts... Read more
Box Sync 4.0.8057 - Online synchronizati...
Box Sync gives you a hard-drive in the Cloud for online storage. Note: You must first sign up to use Box. What if the files you need are on your laptop -- but you're on the road with your iPhone? No... Read more
Audio Hijack 3.8.10 - Record and enhance...
Audio Hijack (was 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... Read more
Direct Mail 6.0.1 - Create and send grea...
Direct Mail is an easy-to-use, fully-featured email marketing app purpose-built for macOS. Create, send, and track great looking email campaigns that get results. Start your newsletter by selecting... Read more

Latest Forum Discussions

See All

SwitchArcade Round-Up: ‘Pokemon Legends:...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for January 28th, 2022. We’ve got a bunch of new releases to look at today, with a few big hitters, a few mid-level diversions, and a healthy supply of compost. Since it’s Friday, we... | Read more »
Phantom Blade: Executioners, S-Game...
S-Game has kicked off its first Closed Beta Test for Phantom Blade: Executioners, inviting a selected few to get first dibs on the upcoming KungFuPunk action RPG on mobile. The CBT officially begins this January 28th, and beta testers will receive... | Read more »
‘Infinite Galaxy’ First Anniversary: Cel...
Cultivating a new generation of valiant commanders across 240 countries worldwide, Infinite Galaxy has quenched players’ thirst to explore the vastness of space – and there are only more intergalactic adventures to embark on from here on out. Camel... | Read more »
War and Order: How to brave the cold in...
War and Order's 6th-anniversary celebrations are underway, and all in good time too - this season not only brings about fabulous festivities, but it also lets players experience the harsh winter in an entirely new way. [Read more] | Read more »
‘Hidden Folks+’ Is This Week’s New Apple...
The original Hidden Folks from Adriaan de Jongh is an excellent hidden objects game featuring hand drawn visuals. It is an absolute joy to play, and it has now released on Apple Arcade in the form of Hidden Folks+ () as an App Store great. If you’... | Read more »
Mini Metro’s First Big Update of 2022 Ad...
Last year saw great updates for Dinosaur Polo Club’s Mini Metro ($3.99) which is also available on Apple Arcade as an App Store Great. | Read more »
SwitchArcade Round-Up: ‘Gunvolt Chronicl...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for January 27th, 2022. In today’s article, we’ve got a whole bunch of new releases to check out. It’s a dangerous one for the wallet today, as there are several interesting games to... | Read more »
SIEGE: Apocalypse lets you engage in mil...
Launching today to the delight of military enthusiasts across the globe, SIEGE: Apocalypse is a new 1v1 military battler from KIXEYE that's set in the early days of the War Commander universe. Players need to collect and upgrade unit cards to build... | Read more »
‘SIEGE: Apocalypse’, KIXEYE’s Military-T...
Military fans across the globe now have more reasons to dive into the War Commander universe as KIXEYE launches SIEGE: Apocalypse on both iOS and Android devices today. The 1v1 military battler pits two players against each other in intense real-... | Read more »
‘Yu-Gi-Oh! Master Duel’ Is Rolling Out N...
Following its launch on PC and all consoles last week, Yu-Gi-Oh! Master Duel has finally released on mobile platforms. Since launch, the game has exploded on multiple platforms with it having over 260k concurrent players on Steam. It has full... | Read more »

Price Scanner via MacPrices.net

Apple has clearance 2020 13″ MacBook Airs ava...
Apple has clearance, Certified Refurbished, 2020 13″ Intel-based MacBook Airs in stock today starting at only $719 and up to $370 off original MSRP. Each MacBook features a new outer case, comes with... Read more
The cheapest iPhones for sale today at Apple...
Apple has restocked Apple Certified Refurbished iPhone 8 models starting at only $359. Each refurbished iPhone comes with a fresh external case, standard Apple 1-year warranty, and free shipping.... Read more
14″ MacBook Pro with Apple M1 Max CPU now in...
Looking for a new 14″ MacBook Pro with an Apple M1 Max CPU? Stock is finally trickling into Apple resellers. B&H has Silver 14″ M1 Max MacBook Pros in stock today for $2899 including free 1-2 day... Read more
14″ MacBook Pros with Apple M1 Pro CPUs are i...
Amazon is reporting stock of 14″ MacBook Pros with M1 Pro CPUs today with a $50 discount. Shipping is free, and delivery is available by February 1st for most configurations. Be sure to make your... Read more
Apple has restocked 13″ M1 MacBook Pros for $...
Apple has restocked a full line of 13″ M1 MacBook Pros available Certified Refurbished, starting at only $1099 and up to $230 off original MSRP. These are the cheapest M1 MacBook Pros for sale today... Read more
Apple’s AirPods Max headphones are on sale fo...
Amazon has Silver, Blue, and Space Gray Apple AirPods Max headphones on sale today for $100 off MSRP. Shipping is free, and all models are in stock today. Their price is the lowest currently... Read more
Open a new line of service at Verizon and get...
Verizon is giving away 64GB Apple iPhone 12 minis or your choice of an iPhone 11 to customers who choose one of these phones and open a new line of service. Offer is available online only, and no... Read more
Open-box 13″ M1 MacBook Airs now available st...
QuickShip Electronics has open-box return 13″ M1 MacBook Airs in stock and on sale for $200-$400 off MSRP on their eBay store right now with free express delivery. According to QuickShip, “The item... Read more
Verizon’s 2022 iPad promo: $100-$310 off any...
Verizon has cellular-capable iPads on sale for $100-$310 off MSRP when purchased with an Unlimited service plan. Sale price is applied to your account monthly over a 24 or 30 month period, depending... Read more
Sunday Sale: Apple AirPods are on sale for up...
Amazon has Apple AirPods on sale for $10-$100 off MSRP today, depending on the model. All are in stock today with free delivery: – AirPods Max headphones (Blue): $449 $100 off MSRP – AirPods Max... Read more

Jobs Board

Registered Nurse (RN) Employee Health PSJH -...
…is calling for a Registered Nurse (RN) Employee Health PSJH to our location in Apple Valley, CA.** We are seeking a Registered Nurse (RN) Employee Health PSJH to be Read more
Systems Administrator - Pearson (United State...
…and troubleshoot Windows operating systems (workstation and server), laptop computers, Apple iPads, Chromebooks and printers** + **Administer and troubleshoot all Read more
IT Assistant Level 1- IT Desktop Support Anal...
…providing tier-1 or better IT help desk support in a large Windows and Apple environment * Experience using IT Service Desk Management Software * Knowledge of IT Read more
Human Resources Business Partner PSJH - Provi...
…**is calling a** **Human Resources Business Partner, PSJH** **to our location in Apple Valley, CA.** **Applicants that meet qualifications will receive a text with Read more
Manager Community Health Investment Programs...
…is calling a Manager Community Health Investment Programs PSJH to our location in Apple Valley, CA.** **Qualified candidates will be invited to do a self-paced video Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.