TweetFollow Us on Twitter

iPhone Productivity Applications, Part I

Volume Number: 25 (2009)
Issue Number: 03
Column Tag: iPhone

iPhone Productivity Applications, Part I

Developing applications that manage complex data

by Rich Warren

(Ed note: Parts I and II were actually printed out of order. Click here for Part II.)

Productivity Applications

My Last article, "Built for iPhone 2.0" described the three general categories of iPhone applications. Utilities provide a single view of the data with little or no interaction. Productivity applications manage and organize complex data. While immersive applications use the entire screen to provide a rich, interactive view. We then looked at the major tools used to build an iPhone application, and stepped through the development of a simple utility app.

This time, we will turn our attention to productivity. Productivity applications focus on managing and organizing complex data. Developers typically refrain from customizing the interface. Instead, they focus on creating a clean, streamlined solution that allows the user to move through the data in an unobtrusive and intuitive manner.

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. Mail is a perfect example of a productivity application. It allows you to manage a number of incoming and outgoing messages from a variety of different email accounts. Many third-party social networking applications also fall into this category. Even the iPod application is largely a data-driven, productivity app.

Productivity applications use three main tools to organize and interact with your data: UITabBarControllers, UINavigationControllers and UITableViews.

UITabBarControllers let the user easily switch between different tasks or different groups of data. For example, the clock application lets the user choose between the different time-related tasks: World Clock, Alarm, Stopwatch and Timer. The iPod app, on the other hand, lets the user select between different views of the data: Album, Artist, Genre, Song, Playlists, Videos, etc. Some applications even mix tasks and data groups. The App Store displays three data groups: features, categories and top 25, while letting the user access the search and updates tasks.

The tab bar can support any number of tab items. However, it will only show five tabs at a time. UITabBarController automatically manages any extra tab items by providing a more... button, and letting the user customize the items that appear on the tab bar.

Next, UINavigationController lets users move from general to more specific views. This controller maintains a stack of views. When a new view is added to the top of the stack, that view slides in from the right, while the old view slides off the left. When the top view is popped from the top of the stack, it slides back off the right, and the previous view returns from the left. This provides a very intuitive interface for moving through hierarchical data.

While both UITabBarController and UINavigationController manage navigation between views, UITableView handles the actual display of data. Tables are often used to present lists, for example, the list of email accounts, the list of mailboxes within a single account, or even the list of messages within a single mailbox. UITableView displays a single column of cells. Each cell within the table can be quite complex, combining images, icons and formatted text. But, in general, the pattern holds. Each entry represents a single selection from a list of similar items.

A productivity application typically combines some or all of these tools into a user interface that naturally matches the application's data. In general, UITabBarController provides the coarsest level of organization, defining our broadest groups. Within each group, UINavigationControllers allows the user to move from the most general to the most specific information, while UITableViews display the information available at each level of detail.

Note: Applications may have multiple UINavigationControllers or UITableViews; however, they should only have a single UITabBarController. Additionally, if you use a tab bar, then UITabBarController should always be the root controller for your application. You can add UINavigationControllers to a UITabBarController, but you should never add a UITabBarController to a UINavigationController.

Gas Tracker

For the rest of this article, we will build a simple productivity application. Given the state of our economy and the recent spike in gas prices, I wanted a tool that would help me track the amount of money I'm wasting on my car.

In many ways, this is an ideal iPhone application. A desktop version just wouldn't work. I'm nowhere near my computer when I fill up my tank. Sure, I've tried carrying a notepad in my car to do these calculations manually, but I always lose the pen under the seat. However, if I'm wearing pants, I have phone with me.

Additionally, the tasks are appropriate for a mobile application. Each interaction is short and simple - perfect for managing data on the go.

Creating the Project

OK, let's get started. Open Xcode. Select File ... New Project.... This brings up the New Project window. Project templates are grouped into categories shown in the leftmost column. Select the iPhone OS Application group.

Apple provides a number of project templates, each pre-loaded with the initial framework for a particular style of application. Since we will use a tab bar to manage the lowest levels of our view hierarchy, we should select Tab Bar Application and press the Choose button. In the next panel, name the application GasTracker, select a path and press Save.


Select the Tab Bar Application Template

Like all Xcode templates, the Tab Bar Application provides a great deal of functionality before we even touch the code. Go ahead and build and run the application. As you see, it creates a tab view that allows us to toggle between two different views.


The code itself starts with the GasTrackerAppDelegate. As with most Cocoa applications, the app delegate manages many of the high-level details. Currently, it simply creates the tab bar controller when the application launches, then releases the controller and window when the application closes.

The delegate also acts as a UITabBarControllerDelegate. There are two commented-out methods for modifying the tab bar's behavior. tabBarController:didSelectViewController: is called whenever the user presses a button on the tab bar. The application also calls tabBarController:didEnd-CustomizingViewControllers: whenever the user edits the buttons on the toolbar. We won't use either of these methods in this application, but they may be useful in your own projects.

The only other class is FirstViewController. Like the app delegate, the initial implementation does not do much, but it has a number of method stubs we could use to customize our view's behavior. We will examine these in more detail later.

There are also two nib files. MainWindow.xib defines the application's Window and Tab Bar Controller. The tab bar's views can either be defined within the Tab Bar Controller, or placed in separate nib files. In this template, the first view is defined within the controller. Open the MainWindow.xib , and then open the Tab Bar Controller. Click on the First button. You will see the layout directly.

The second view is defined in its own nib, named (not surprisingly) SecondView.xib. Click on the tab bar's Second button, and you will see a grayed-out box in the center of the view, with a link to SecondView.xib. Double click the link, and the second view's nib will open automatically.

While the generated code demonstrates a few useful techniques, the initial files are not very useful. I think it's better to just start from a clean slate. First, let's handle the simple part. In Xcode, delete FirstViewController.h, FirstViewController.m, and SecondView.xib. Go ahead and move them to the trash when prompted.

Now for the trickier part. In Interface Builder, open MainWindow.xib, and click on the Tab Bar Controller from the Interface Builder Documents.


Interface Builder Documents

Make sure the Attributes Inspector is open (either through the Tools menu, or by clicking on the Inspector's Attributes tab). You should see a list of view controllers that have been added to the tab bar. Select First, and then click the minus button. It should be deleted from the list. Do the same for Second.


Now let's add our views to the tab bar. We can set both the name and the class (or at least the super class) in this table. Press the plus button. Name this view History, and set the class to Navigation Controller. Now add five more views. Name these MPG, Gas Price, Cost/Mile, Cost/Month and Cost/Day. Leave the classes set to View Controller.

Note: You should be a little careful with the button titles. If they are too long, the labels may appear cramped or even overlap. Also, the size shown in Interface Builder is different from the size in the actual app. Build and run the application to test the label's true size. According to my tests, Cost/Month is OK, but Cost Per Month was too long.


Now look at the Tab Bar Controller window again. Notice, if you click the button once, the window will display the view associated with that button. All the views are currently grayed out. Additionally, the Inspector will show the view controller settings for that view. Double click the button, and the inspector shows the settings for the tab bar item itself.

Building Controllers and Nibs

Before we can do any more work in Interface Builder, we need to create our nibs and controller classes. Let's start by adding the controllers. Back in Xcode, right click on the Classes folder in the Groups & Files view. Select Add ... New File.... In the New File window, make sure Cocoa Touch Classes is selected, and then double click on NSObject subclass. In the next screen, name it HistoryNavigationController.m and click Finish. Do the same for StatsViewController.m, but this time, select the UIViewController subclass.


Adding a UIViewController Subclass

NSObject may seem like a strange choice for our HistoryNavigationController, but we don't need the method stubs provided by the UIViewController template, and we're going to change the super class anyway. So, open HistoryViewController.h and make the changes shown below:

HistoryNavigationController.h

Initial interface for the HistoryNavigationController class.
#import <UIKit/UIKit.h>
@class Model;
@interface HistoryNavigationController : UINavigationController {
   IBOutlet Model *model;
}
@property (nonatomic, retain) Model *model;
@end

We've made two small changes. As we already indicated, we've changed the super class to UINavigationController. Second, we've added a Model property, including a forward declaration of the Model class, and a model instance variable. The model is also declared as an IBOutlet, which allows us to connect it to the controller using Interface Builder. What's Model? Hold that question; we'll get to that in a bit.

While we're here, take a closer look at the property definition. We are using both the nonatomic and the retain keywords. The first keyword is an easy choice. Most iPhone properties should be declared nonatomic. They typically do not need to be thread safe, and using nonatomic accessors improves performance.

The second keyword is more of a design choice. By default, properties are set to simply assign incoming values to the instance variable. That may be appropriate if you can guarantee that the object in question will be retained somewhere else in your application. In our case, the model object will be instantiated by our nib file.

In iPhone OS, the nib file autoreleases all objects it creates. However, if you connect an object to an IBOutlet, it will call setValue:forKey: which will use the appropriate setter method. For our model, it will use the setter defined by the property, so it will be retained. Additionally, if you don't provide an explicit setter, the object would be retained automatically.

Note: The rules defining how nibs retain objects differ from Mac OS X to iPhone OS. In both cases, Apple recommends that you manage these objects through an IBOutlet, and create an appropriate setter that explicitly retains the object. For more information, see the Resource Programming Guide, which can be found either in Xcode's help documentation or online at http://developer.apple.com.

Ok, let's look at the implementation. Open HistoryNavigationController.m. We need to make three small changes here. Import Model.h, synthesize the model property, and release the model as shown below.

HistoryNavigationController.m

Initial implementation for the HistoryNavigationController class.

#import "HistoryNavigationController.h"
#import "Model.h"
@implementation HistoryNavigationController
@synthesize model;
- (void)dealloc {
    [model release];
    [super dealloc];
}
@end

Now add the model property to the StatsViewController. It follows the same pattern: forward declaration, instance variable and property declaration in StatsViewController.h; import, synthesize and release in StatsViewController.m. I will leave the actual typing up to you.

While you're there, you'll probably notice that StatsViewController.m contains a number of method stubs. These handle a range of different events: custom initialization, rotation, and low memory warnings. We'll look at these settings in more detail in part two.

For now, let's build our nib. At this point, we'll just create a single nib for our stats view controller. Right click on the Resources folder and select Add ... New File.... Make sure User Interfaces is selected, and double-click View XIB. Name it StatsView.xib.

Open StatsView.xib. In the Document Window, select File's Owner. Open the Identity Inspector, and change the class to StatsViewController. Next, connect the File's Owner's view outlet to the View object in the Document Window. You can do this by right clicking on the File's Owner icon. A black and gray table will appear. Click in the circle at the end of the view row, then drag to the View icon. You can now save and close the nib file. We will edit the layouts later.


Connecting the File's Owner's view Outlet

Note: Nibs are traditionally placed in the Resources group; however, you do not have to place them there. The groups are only used within Xcode to organize and manage the source files. They have no relationship to the actual layout of the project's files on disk. In fact, you can create your own groups to further improve the project's organization. If you download the source code for this project from ftp.mactech.com, you will see that I've created a number of additional subgroups within the Classes group.

Ok, now let's link our tabs to these nib files. Make sure MainWindow.xib is open. Select the Tab Bar Controller icon in the Document Window. This will open the Tab Bar Controller view, if it isn't open already. The tab bar displays the six views that we just added. Single click on the History tab. The Attributes Inspector should be labeled as Navigation Controller Attributes. If not, try clicking on another tab, then single clicking on the History tab again.


The Nib Name attribute here would be for the Navigation Controller itself. We won't use a nib for this controller, so just leave this entry blank. We will eventually modify the navigation controller's layout directly in the Tab Bar Controller view. Switch to the Identity Inspector (either through the Tools menu, or by clicking on the Inspector's Identity tab), and change the class to HistoryNavigationController.



Now, single click on the MPG tab. This time, we should set the nib name to StatsView and set the class to StatsViewController. Repeat this for all the remaining tabs.



Now let's set the icons for each tab. History is the easiest. Make sure the Attribute Inspector is open. Double click on the history tab to bring up the Tab Bar Item Attributes. Change Identifier to History. This will set both the tab title and the icon to the built-in History tab item.


Note: Each of the built-in icons is automatically linked with a title. You cannot use the icon without using the corresponding title. If you want a custom title, you must use a custom icon.

For the other tabs, we will customize the icons and titles. Tab bar icons are simply 30-pixel by 30-pixel PNG image. Notice, however, that the image color does not matter. The tab bar uses the alpha value of each pixel to create a monochrome icon. It displays the alpha channel as blue in the tab bar, and as black in the More... list.

To create these icons, I find it easiest to use a paint program that supports transparencies. Draw the icon in black against a transparent background. You can even set the transparency of the ink to draw in grayscale. Then save the image as a PNG.

30 x 30 Icon Art, Black with a Semi-Transparent Gray Background


Once you have an icon, add it to your project. In Xcode, select Project ... Add to Project.... Navigate to the PNG file and click Add. In the next window, be sure to check Copy items into destination group's folder (if needed).

Now, back in Interface Builder. Double click on the tab in question. Identifier should say Custom. Select your icon's name from the Image combo box. Repeat for each tab.


OK, I know...I know...I've gone through a lot of Interface Builder commands rather quickly. Hopefully, I've given you enough pointers to get by, but a complete tutorial on Interface Builder is really beyond the scope of this article. If you have any questions or difficulties, check out Apple's Interface Builder User Guide. That can be found either in Xcode's developer documentation, or online at http://developer.apple.com.

M is for Model

Apple really pushes the Model View Control pattern (MVC to the cool kids). I won't go into a full description of MVC here. If you're not familiar with the concept, there are plenty of great explanations on the web. I'll just say that I agree with Apple. MVC is a good idea. [Ed. Note: For a review of MVC, see Dave Dribin's "Road to Code" in the August, 2008 issue]

We've already seen two of the three main players. We've just built a number of views and controllers. We've linked them together. All that with only a few lines of code.

But now things get a little weird. While Cocoa provides clear support for the controllers and views, models aren't so obvious, at least not on the iPhone. Here's the problem. Most of our controllers will need a connection to our model. Some of these controllers may be instantiated pragmatically, letting us pass the model to them directly, but many are automatically created through the nib files.

There are a number of ways to work around this problem. Many people simply instantiate their model object in the app delegate, and then add a property to the delegate. They can then access the property anywhere in their project, using the application singleton.

Using the UIApplication Singleton

Add your model to the application delegate as a property. You can access the application Singleton anywhere in your project. From the application, you can access the delegate, and from the delegate you reach your model.

UIApplication *app = [UIApplication sharedApplication];
id delegate = app.delegate;
id modelObject = [delegate model];

This works, and it is reasonably simple. However, there are a number of reasons I don't like it. First, singleton classes have many of the same problems as global variables. Second, we're programming against a particular application delegate's implementation, not the UIApplicationDelegate protocol. Third, from a pure Object Oriented standpoint, managing the model seems outside the application delegate's role. Finally (and most importantly), it just feels kludgy.

Alternatively, we could create a singleton object to explicitly manage our model. While this feels better in many ways, I still prefer to avoid Singletons where possible. Besides, it may be a bit heavy handed for this application. Though, I will admit, it may be the cleanest solution for some of the more-complicated projects.

If possible I'd like to connect the model the same way we connected the views and controllers, using Interface Builder. Fortunately, in this application we can create a single Model object in our MainWindow.xib, and connect it to all the relevant controllers. This may not always be possible. In more complex projects, you may need to pass an external object into a nib as a proxy object. These objects are then set when you load the nib file using NSBundle's loadNibNamed:owner:options: method. For more information, check out the NSBundle UIKit Additions, Resource Programming Guide and Interface Builder User Guide.

The first step is to build our model. Unfortunately, that is outside the scope of this article. Instead, you can simply grab the necessary code from ftp.mactech.com. You will need both the Model and the Entry classes. Add these to your project.

Note: For this project, our model knows how to automatically load any saved data when it is instantiated. It will also save itself whenever the user makes any changes. This greatly simplifies our controller and delegate code. Check out Model.m for more details.

Now let's add the object to our nib. Open MainWindow.xib. Drag an Object controller from the Library to the Document Window. In the Identity Inspector, set the Object's class to Model. Now we have to make our connections. Single click on each tab to select the tab's controller, then right click on the tab. Connect the tab's model outlet with the model object. That's it. We've linked our model, views and controllers.

Configuring Tab Customizability

As I said earlier, the tab control can only display up to five icons at a time. If you have more than five view controllers, the tab controller only displays four of them and a More... button. Clicking on the More... button brings up a table with all the additional controllers. The user can then select a controller from the table, or they can click on the edit button and customize the contents of the tab bar.

However, not all tab items are customizable. For example, the user cannot replace the More... button. In fact, UITabBarController maintains an array of customizable controls. If a controller is in that array, then it can be moved or removed from the tab bar. By default, all the tab bar's view controllers are automatically placed in the customizeableViewController array.

In our program the History view will act as the main interface for managing our data. It should always be available, therefore we must remove it from the customizeableViewController array. We could subclass UITabBarController, but that's probably overkill for this application. Instead, we can access the tab bar controller from within our application delegate.

Open GasTrackerAppDelegate.m, and modify the applicationDidFinishLaunching: method, as shown below:

applicationDidFinishLaunching:

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    
    // Add the tab bar controller's current view 
    // as a subview of the window
    [window addSubview:tabBarController.view];
   
      
    // Only the StatsViewControllers are customizeable
    NSMutableArray *customizeable = [[NSMutableArray alloc] init];
    for (id controller in tabBarController.customizableViewControllers) {
      
        if ([controller isKindOfClass: [StatsViewController class]]) {
            [customizeable addObject:controller];
        }
    }
   
    tabBarController.customizableViewControllers = customizeable;
    [customizeable release];
}

Basically, this method creates a new array that only contains subclasses of StatsViewController, then it assigns that array to the UITabBarController's customizeableViewControllers property.

OK, a little bit of housecleaning left. Be sure to import StatsViewController.h at the top of GasTrackerAppDelegate.m.

#import "GasTrackerAppDelegate.h"
#import "StatsViewController.h"

Looking Forward:

So far, we've built the application's skeleton and set up the tab view and the model. The application compiles without any warnings. When you run the app, you can switch from tab to tab or customize the tab bar. Of course, the various views don't do much yet.

This project will continue in Part 2. In particular, we will focus on setting up the navigation controller and our table views. We will also add a view for entering data, and create custom classes for each of the stats views. Once that's done, we'll have a fully functional productivity application.


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

Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links... | Read more »
Price of Glory unleashes its 1.4 Alpha u...
As much as we all probably dislike Maths as a subject, we do have to hand it to geometry for giving us the good old Hexgrid, home of some of the best strategy games. One such example, Price of Glory, has dropped its 1.4 Alpha update, stocked full... | Read more »
The SLC 2025 kicks off this month to cro...
Ever since the Solo Leveling: Arise Championship 2025 was announced, I have been looking forward to it. The promotional clip they released a month or two back showed crowds going absolutely nuts for the previous competitions, so imagine the... | Read more »
Dive into some early Magicpunk fun as Cr...
Excellent news for fans of steampunk and magic; the Precursor Test for Magicpunk MMORPG Crystal of Atlan opens today. This rather fancy way of saying beta test will remain open until March 5th and is available for PC - boo - and Android devices -... | Read more »
Prepare to get your mind melted as Evang...
If you are a fan of sci-fi shooters and incredibly weird, mind-bending anime series, then you are in for a treat, as Goddess of Victory: Nikke is gearing up for its second collaboration with Evangelion. We were also treated to an upcoming... | Read more »
Square Enix gives with one hand and slap...
We have something of a mixed bag coming over from Square Enix HQ today. Two of their mobile games are revelling in life with new events keeping them alive, whilst another has been thrown onto the ever-growing discard pile Square is building. I... | Read more »
Let the world burn as you have some fest...
It is time to leave the world burning once again as you take a much-needed break from that whole “hero” lark and enjoy some celebrations in Genshin Impact. Version 5.4, Moonlight Amidst Dreams, will see you in Inazuma to attend the Mikawa Flower... | Read more »
Full Moon Over the Abyssal Sea lands on...
Aether Gazer has announced its latest major update, and it is one of the loveliest event names I have ever heard. Full Moon Over the Abyssal Sea is an amazing name, and it comes loaded with two side stories, a new S-grade Modifier, and some fancy... | Read more »
Open your own eatery for all the forest...
Very important question; when you read the title Zoo Restaurant, do you also immediately think of running a restaurant in which you cook Zoo animals as the course? I will just assume yes. Anyway, come June 23rd we will all be able to start up our... | Read more »
Crystal of Atlan opens registration for...
Nuverse was prominently featured in the last month for all the wrong reasons with the USA TikTok debacle, but now it is putting all that behind it and preparing for the Crystal of Atlan beta test. Taking place between February 18th and March 5th,... | Read more »

Price Scanner via MacPrices.net

AT&T is offering a 65% discount on the ne...
AT&T is offering the new iPhone 16e for up to 65% off their monthly finance fee with 36-months of service. No trade-in is required. Discount is applied via monthly bill credits over the 36 month... Read more
Use this code to get a free iPhone 13 at Visi...
For a limited time, use code SWEETDEAL to get a free 128GB iPhone 13 Visible, Verizon’s low-cost wireless cell service, Visible. Deal is valid when you purchase the Visible+ annual plan. Free... Read more
M4 Mac minis on sale for $50-$80 off MSRP at...
B&H Photo has M4 Mac minis in stock and on sale right now for $50 to $80 off Apple’s MSRP, each including free 1-2 day shipping to most US addresses: – M4 Mac mini (16GB/256GB): $549, $50 off... Read more
Buy an iPhone 16 at Boost Mobile and get one...
Boost Mobile, an MVNO using AT&T and T-Mobile’s networks, is offering one year of free Unlimited service with the purchase of any iPhone 16. Purchase the iPhone at standard MSRP, and then choose... Read more
Get an iPhone 15 for only $299 at Boost Mobil...
Boost Mobile, an MVNO using AT&T and T-Mobile’s networks, is offering the 128GB iPhone 15 for $299.99 including service with their Unlimited Premium plan (50GB of premium data, $60/month), or $20... Read more
Unreal Mobile is offering $100 off any new iP...
Unreal Mobile, an MVNO using AT&T and T-Mobile’s networks, is offering a $100 discount on any new iPhone with service. This includes new iPhone 16 models as well as iPhone 15, 14, 13, and SE... Read more
Apple drops prices on clearance iPhone 14 mod...
With today’s introduction of the new iPhone 16e, Apple has discontinued the iPhone 14, 14 Pro, and SE. In response, Apple has dropped prices on unlocked, Certified Refurbished, iPhone 14 models to a... Read more
B&H has 16-inch M4 Max MacBook Pros on sa...
B&H Photo is offering a $360-$410 discount on new 16-inch MacBook Pros with M4 Max CPUs right now. B&H offers free 1-2 day shipping to most US addresses: – 16″ M4 Max MacBook Pro (36GB/1TB/... Read more
Amazon is offering a $100 discount on the M4...
Amazon has the M4 Pro Mac mini discounted $100 off MSRP right now. Shipping is free. Their price is the lowest currently available for this popular mini: – Mac mini M4 Pro (24GB/512GB): $1299, $100... Read more
B&H continues to offer $150-$220 discount...
B&H Photo has 14-inch M4 MacBook Pros on sale for $150-$220 off MSRP. B&H offers free 1-2 day shipping to most US addresses: – 14″ M4 MacBook Pro (16GB/512GB): $1449, $150 off MSRP – 14″ M4... Read more

Jobs Board

All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.