iApps Development Blog
iApps Development Blog
UI Lessons From My First App
[This is going to be a long post, so get comfy.]
If you have an idea for what you think is a great piece of software, it’s hard to curb your enthusiasm — and keep your eye off the calendar. You feel pressure to get the thing out as quickly as possible because you’re proud of the idea and worried that someone else with the same idea will beat you to the App Store. In the iPhone software world, the Gold Rush mentality swirling around the community stokes the pressure-cooking boiler all the more. “Gotta get it out now!” The hazard of succumbing to that pressure is that you may be lured into skipping over subtle, yet vitally important user interface features. What makes it so hard to invest extra time into the finer UI points is that if done right, the user won’t even notice that the features are there; conversely, if you leave out the features, the users will scream bloody murder.
I’ll use my little soapbox here to expose several of the design decisions I made in the development of iFeltThat. The app uses standard UI classes of the iPhone SDK, so the following probably has little to offer developers of games or other highly custom-graphical environments. My comments will also probably reveal my newbieness in iPhone software development. Having won three Software Publisher’s Association (remember them?) awards in Stone Age 1987 for my first two commercial HyperCard stacks, I’m not exactly a babe in the woods when it comes to producing finished commercial software. But I had to (and continue to) learn a lot (C, Objective-C, XCode, iPhone SDK APIs) to bring my first iPhone app idea to fruition. (Future installments of this blog will talk about my transition from scriptboy to hard-core programmer.)
It will help to be familiar with iFeltThat to get the most from the rest of this post. If you don’t have the app already, you can see it in action in the three videos.
Challenge Number One
When starting on a new app, I probably spend more time thinking about the basic user interactivity structure of the program than anything else. I did it for iFeltThat as well as a new app I’ve nearly finished. Choosing New Project... in XCode comes after a lengthy thought process and lots of pencil-and-paper sketching.
The information structure for iFeltThat’s primary entry into the program is essentially a very shallow, very broad hierarchy. I wanted to present lists of recent earthquake events for more than a dozen distinct geographical regions. The easy way would have been to set up the first screen as a UITableView, with each cell representing one of the regions...tap a cell to drill down and view the earthquakes reported for that region.
I rejected that idea very quickly. The problem with hierarchies is that you can’t easily jump from item to item at random without tracing your way back to a next higher level. In other words, for a user to get from, say, San Francisco to Alaska lists, she’d have to go back to the top-level list. Unacceptable.
Because the program had so many region lists, using a toolbar with items representing regions was also out of the question. I also didn’t like my visualization of a UIPickerView modally popping up to change lists — it seemed too intrusive.
Then I looked at the UITabBar. Although Apple’s guidelines suggest that items in a tab bar represent modes — and usage in the iPod and Phone applications support that model — each of my region lists felt like a mode to my brain. Moreover, the UITabBar is a very efficient use of precious screen real estate for switching among more than a handful of options.
UITabBar Bonanza and “Burden”
It was clear that the number of regions I intended to present would outgrow the maximum of five tab bar items displayed on the iPhone. Thanks to someone’s foresight in iPhone OS development, the UITabBar facilities gave me a ton of “freebies.” Most importantly, if the number of tab bar items exceeds the five spaces, the OS turns the last item into a “More...” choice, which automatically leads to a table view of remaining items. You can even make that list editable, leading to the Configure screen that allows a user to drag tab bar items into any desired position on the UITabBar. One line of code makes your tab bar look like genius design!
This plan worked well for the way I thought users would typically configure their copies of iFeltThat. They’re probably interested in only a handful of regions to which they’d like one-tap access in the tab bar. No matter what their geographical interests, they could configure the tab bar items accordingly. Super!
All was not perfect, however. My original list of regions grew to nearly 20, but the Configure screen provided by the OS does not scroll. This limits the total number of items to 16 (a row below the fully visible items is only partially visible). I had to do some trimming to keep my regions to the acceptable maximum.
In my experience with Apple’s own apps that employed the UITabBar, I recognized that my initial implementation lacked persistence when it came to a) the customized tab bar item order and b) which tab bar item was being viewed when the app last closed. To assist with the former, a tab bar invokes delegate methods that signal a user’s customization of the order. I just needed to devise a scheme that let me save the order in NSUserDefaults and use that order at next launch to sort the array of items eventually assigned to UITabBarController.viewControllers. The selected index of the UITabBar is also carried over from one session to the next via NSUserDefaults.
Although these extra steps weren’t that difficult to build into the app, not everyone does it. One of my local TV stations has an iPhone app that presents somewhat of a user interface in front of various California Department of Transportation traffic cameras around the San Francisco Bay Area. They chose to implement a UITabBar primary interface with just two items: one for the whole alphabetical list, and one for a user-generated sublist. But every time the app launches, the complete list appears, forcing a press of the tab bar item to get to my regular short list. Additionally, the programmer failed to make the customized list manually sortable. Items in the list remain frustratingly alphabetical. Wouldn’t it be smarter to let users set the list according to their usual commute routes? For the lack of an hour or two of programming (assuming an inexperienced iPhone SDK coder), the application is frustratingly — if not dangerously at 65 mph — hard to use.
Flattening Hierarchies
By utilizing the UITabBar style of interface, I essentially eliminated a hierarchy level in the program’s organization. There was no “lobby.” Instead, when the program launches, you go directly to the last “room” you were viewing in the last session. To get to another favorite room, tap a single tab bar item.
But another hierarchy existed. Tapping a cell to drill down to an earthquake’s detail view meant that viewing other events in the same region would require navigating back to the list. A pair of buttons in the detail view would allow a user to navigate up or down the list to an adjacent (in time) earthquake, but how to label those buttons?
Because the lists were in reverse chronological order, I felt that up and down arrows in the detail view would be ambiguous. Would an up arrow mean higher up the list or further back in time? I could never be sure, and I was writing the darned thing.
My solution was to use buttons with clear (and localizable) labels for “Older” and “Newer.” Their relative positions on the list were irrelevant, but their temporal relationship to the current event was certainly easy to comprehend. One additional touch: when you reach the end of the line in any one direction, the button becomes visibly inactive, indicating there aren’t any newer or older items in the list. A small touch that eliminates potential frustration.
Taking iFeltThat Offline
Although iFeltThat relies entirely on earthquake data retrieved from servers, I am fully aware that users aren’t always tethered to the Internet. They forget to turn off Airplane Mode. They wander into areas where cell coverage is marginal, overtaxed, or nonexistent. They can’t find a WiFi hotspot for their iPod touches when they’d like it most. What was iFeltThat going to do when its main data lifeline is severed?
Several built-in applications provided the answer: Save the most recently-retrieved data locally, and use that when a connection to fresh data isn’t available. As I look over my source code, it’s clear that I knew I had to do this from the earliest designs. I had set up my internal data structures knowing full well that I’d be saving and reading object data in the local file system.
Better still, the program displays saved data right away to get something on the screen while new data is being retrieved, parsed, and displayed. That model works in Mail, largely with the help of the “Updated” field in a UIToolbar at the bottom of the screen. That leads me to...
Temporal Lobes
I had a few date-and-time-based items to handle in iFeltThat. The first was how to let users know whether the data they were viewing in the region lists was brand new or an archived set. Perhaps influenced by my own experience with the iPhone, I believed users were comfortable with the way the built-in Mail program handled that issue. At the risk of eating up yet more data display space with a third part, I added a UIToolbar just above the UITabBar. It provided space for the “Updated” time and date, as well as a control to force an update, just as in Mail.
A second date-and-time issue involved displaying the date of earthquake items, both in the lists and in the detail views. I’ve always had problems with programs that try to be user friendly for things like “Today” and “Tomorrow” but then switch to hard dates outside of that range (the eyeTV program and schedule lists do that on my Mac, and it drives me nutso). This is especially problematic on the iPhone because only the current time is displayed in the top status bar, not providing a handy at-a-glance reference to today’s date. Ask me twenty times a day for today’s date, and I’ll have to think a bit and come up with the wrong date at least 25% of the time.
For this application, the dates are associated with earthquake events. List items are always displayed in reverse chronological order. By and large, I believe users will be interested in how long ago something happened, rather than a specific date for entry into their journals. I decided, therefore, to display dates not as dates, but as “Today,” “Yesterday,” or “n Days Ago.” It’s a bit of a non-traditional gamble on my part, but my gut tells me it’s the right way to go.
One last time item had to do with displaying the time of an event. The Internet data sources supply dates and times of events in a variety of time zones. Some are localized to the region (Central, Mountain, and Pacific zones), while others are in Greenwich Mean Time. Regardless of where the user is located, the most meaningful time is that person’s local time. I believe the general population is more time-zone-challenged than geography-challenged. Again, I made another unilateral decision to display event times in the iPhone’s local time (as established in the device’s Settings panel). Learning yet another system of date and formatting objects in the iPhone SDK was just part of the fun.
Drill, Baby, Drill!
A seemingly minor UI decision was how to handle taps in cells of the region lists. Although it was a close call, I decided to use the more colorful disclosure button because the destination view was not another table.
In my early designs, I activated only the disclosure button for tapping. The more I used the development versions myself, the more I felt that was too restrictive. In the end, I made both the entire cell and disclosure button drill down to the detail view associated with the table cell.
I mention this because of some things I observed as a result. One of my early testers got used to tapping the disclosure button when it was the only game in town. Watching him use the finished version recently, I see that he still studiously aims for the button. In contrast, I let another friend of mine (not a tester) try the app on my iPhone. For whatever reason, she was instinctively drawn to the colorful squares with the magnitude numbers at the left edge of table cells. That’s where she tapped to drill down to the detail view. My design choice had them both covered, and there was no head scratching when it came to viewing a quake’s details. Disaster averted by a smidge of forethought.
The Maps — Oy!
I was determined to have detail maps of earthquake events integrated into the program. Forcing users to leave the program to see an event’s location in the built-in Maps application was simply too disruptive to even consider. I wanted users to float smoothly among events as quickly as possible (e.g., tracking a major event and associated aftershocks).
As best I could determine, the iPhone SDK does not give ordinary newbies access to the built-in Maps application beyond launching it as an external program. Programs like Weather Bug, which shows radar map overlays atop Google maps, is doing something (probably at their own server) beyond what I could discover.
In the end, I had to make the biggest compromise of the entire program — using Google’s web-based API via a page at one of my own web servers. Thus, I had to build in a UIWebView which loaded a page of my own design, which, in turn, loads Google JavaScript code to fetch a Google map at a certain location and zoom level. My own page and JavaScript code (I knew my 12 years of JavaScripting would come in handy) handled the overlays of markers for the quake and iPhone locations.
On the one hand, I was satisfied that the maps in the UIWebView looked enough like the maps seen in the Maps application — there is comfort in consistency. But these web maps aren’t pinchable like the Maps app. A Google-ite programmer had cobbled together some Objective-C code that made it possible to simulate the two-fingered UI atop the web view, but the performance was not to my liking.
Instead, I discovered that applying Google’s zooming feature was comparatively swift. I blocked events to the UIWebView (which also prevented the annoying web content drag-gliding) and overlaid two semi-transparent buttons that let users zoom in and out one level with each tap. I even went the extra two feet to hide a zoom button when it was no longer possible to zoom in that direction — again, one of those things no one will notice by its implementation, but would cause frustration when a user tries to tap to zoom beyond what was possible.
Performance in loading the maps is not as fast as I’d like, but acceptable with a WiFi or true 3G connection. Demos on my Edge iPhone...well, I’ve learned a little soft-shoe number. Some third-party map alternatives are in the offing, and I’ll be investigating them (and their licensing issues) for future versions.
Even though iFeltThat’s maps are less than perfect, I didn’t want to lock users out of a complete Maps experience if they wanted it for satellite views and such. In fact, from a detail view, you can bring up a menu, all of whose items branch out to other programs: Safari, Maps, and Mail. I’d like to claim massive prescience in being able to group all external links to this one menu, but that would be a lie. It was more luck than anything else. I think that happens sometimes when uniting a user interface to a program’s functions.
Preparing for UI Decisions
The best way to sum up this experience is to highlight what I believe are the principles that led me to make the decisions I did — or even know that there were decisions to be made. In a sense, most of these are iPhone-icized versions of principles I’ve used in the past.
First, before thinking about writing apps, I try to be an incessant user of the platform for which I’m developing. This was easy with the iPhone, having been a fanboy since Night One. Even my early, pre-SDK web app dabbling (yes, the first iPhone tip calculator) came only after having used the built-in apps hour after hour after hour to get a sense of what users expected from the environment.
Second, once I had the basic structure and operation of iFeltThat working in the Simulator, I started loading it into real devices. The first time you do this, you are amazed at how different things look in the denser pixels of the iPhone or iPod touch. Some small text and images you thought perfectly readable in the Simulator will cause your users eye strain.
Third, even before you start handing out copies to beta testers, hand your iPhone or iPod touch to your friends and observe them closely as they use your development version. Look for those moments of hesitation (“What do I do now?”), frustration (“Nothing happens when I tap here.”), and squinting (“What does that say?”). Some of these issues may be serious enough to cause you to reconsider major design decisions. The earlier you catch these — things you’ll never see because you know too much about how things work in the code — the better.
Finally, once you’ve loaded the program into your own device, use the hell out of it in all types of situations. Try to turn off your brain and become a lazy user. A well-designed program will work miracles for a lazy user because the program’s designer anticipated that laziness. The program just does the right things. Be completely thoughtless in the way you use the program. If your app relies on location services or the internet, turn off those features in Settings; when prompted to turn them on, say no, and see what happens next — because users inevitably will tap the “wrong” button. Every sigh you emit because something didn’t work the way you’d like is something to fix or rethink.
Just as you can use well-executed applications for inspiration on what to do, keep an eye out for bad apps that glaringly display the wrong ways to do things. I think back to that traffic camera application. It appears to have been implemented on a number of smart phone platforms, with the iPhone being the last one out the door. I have this vision of a programmer hastily porting the application from a Blackberry to the iPhone SDK with little iPhone experience. Teeth gritted firmly, he burrows his way through the iPhone SDK docs to get the minimum functionality going, giving short shrift to the user experience.
Don’t be that guy.
Thursday, February 12, 2009