How to Make a Gesture-Driven To-Do List App Like Clear: Part 1/3

This is a post by Tutorial Team Member Colin Eberhardt, CTO of ShinobiControls, creators of playful and powerful iOS controls. Check out their app, ShinobiPlay. You can find Colin on Google+ and Twitter This three-part tutorial series will take you through the development of a simple to-do list application that is free from buttons, toggle […] By Colin Eberhardt.

Leave a rating/review
Save for later
Share

Learn how to make a stylish gesture driven to-do app like Clear!

Learn how to make a stylish gesture driven to-do app like Clear!

This is a post by Tutorial Team Member Colin Eberhardt, CTO of ShinobiControls, creators of playful and powerful iOS controls. Check out their app, ShinobiPlay. You can find Colin on and Twitter

This three-part tutorial series will take you through the development of a simple to-do list application that is free from buttons, toggle switches and other common user interface (UI) controls.

Instead, users will interact with your app via a set of intuitive gestures, including swipes, pull-to-add, and pinch. In eschewing the common interface components, you’ll present the user with a more striking and clutter-free interface. It’s not an empty gesture!

This tutorial is for intermediate or advanced developers – you will be doing some tricky things like working with gradient layers, performing animations, and even creating a custom table view. If you are a beginner developer, you should start with some of our other tutorials.

If you want to make better use of gestures in your application, then this is the tutorial for you. Read on to start the hand aerobics!

Skeuomorphism and Touch Interfaces

Before diving into the code, it’s worth taking some time to discuss the role of gestures in UI design. Don’t worry – it’s a “gripping” topic!

The mobile multi-touch interface allows for much more direct interaction – and therefore much more control and expression – than does a simple mouse pointer device.

Some very cool and intuitive gestures have been developed, such as pinch/stretch, flick, pan, and tap-and-hold. But they are rarely used! (One notable exception is the pinch/stretch, which has become the standard mechanism for manipulating images.)

Despite the expressive nature of touch, we developers still fall back on the same old UI paradigms of buttons, sliders, and toggle switches. Why?

One of the reasons we continue to use these same-old UI components is due to a design philosophy known as skeuomorphism.

Ragecomic

To help users understand a visual computer interface, we design UIs to look like physical objects that the user is already familiar with. Apple has thoroughly embraced skeuomorphic design in its own applications, achieving almost photo-realistic representations of physical objects, such as notebooks and bookshelves.

But hey – designs can evolve as readily as technology. Graphical computer interfaces have been around for 40 years. Isn’t it time we ask ourselves, “Are buttons really necessary?”

I thoroughly recommending watching Josh Clarke’s presentation “Buttons are a Hack”, wherein he encourages developers to think more creatively about gestures and touch interactions. The next time you go to add a new control to your interface, ask yourself, “Can I perform the same function via touch?”

When an application comes along that makes good use of gestures, it is quite striking. A recent example is
Clear by Realmac software. Be sure to check out the great demo on YouTube, or even better download the app to check it out.

This tutorial describes the development of a to-do list application that is very similar to Clear. The purpose of this tutorial is to encourage you to think about how to make better use of gestures in your own applications, rather than to create a clone of Clear. I encourage you to download and buy Clear, as it is a truly inspirational app.

Anyhow, I think it’s time I climbed down from my soapbox and showed you all some code!

Getting Started

Fire up Xcode and create a new iPhone application by going to File\New\Project, selecting the iOS\Application\Single View Application template and tapping Next. On the next screen, enter ClearStyle as the product name, and fill in the other details similar to the image below:

Note that you’ll use Automatic Reference Counting (ARC), but not Storyboards, as this is a single-page application. Also note that a Class Prefix is set here – you can omit that, but if you do, be aware that the auto-generated names for some files will be different from what’s specified in this tutorial.

A to-do list is essentially a list of items rendered on the screen. The standard approach to rendering scrollable lists within an iPhone application is to use a UITableView. So you’ll next add one of these to the view controller that was created as part of the project template.

Click on SHCViewController.xib in order to open the Interface Builder and drag a table view onto the view surface:

In order to access the UITableView, you have to add a referencing outlet. So, bring up the Assistant Editor (tap on the middle button in the Editor group of buttons on the top-right of the Xcode toolbar) and control-drag from the table view onto SHCViewController.h, as shown below. Name the outlet tableView:

And with that, your minimalist user interface is complete!

The eagle-eyed among you might be wondering why I used a UITableView within a UIViewController – why not use a UITableViewController? Without giving too much away, let me just say that in the next part of this tutorial, you’ll be replacing the UITableView with your own custom implementation. So there’s a method to my madness. :]

To render a list of to-dos, you need to create an object that represents each to-do item. So let’s do that!

Right-click the project root in the Project Navigator and select New File…, then select the iOS\Cocoa Touch\Objective-C class template and add a class called SHCToDoItem. Make it a subclass of NSObject:

Open SHCToDoItem.h and add a couple of properties and methods (between the @interface and @end lines):

// A text description of this item.
@property (nonatomic, copy) NSString *text;

// A Boolean value that determines the completed state of this item.
@property (nonatomic) BOOL completed;

// Returns an SHCToDoItem item initialized with the given text. 
-(id)initWithText:(NSString*)text;

// Returns an SHCToDoItem item initialized with the given text.  
+(id)toDoItemWithText:(NSString*)text;

A to-do item is simply a string of text and a Boolean that indicates whether the item is complete or not.

If you’ve followed some of the older tutorials on this site, you might expect to synthesize the properties you just added. But surprise! You don’t have to do that any longer with Xcode 4.5 (and you are using Xcode 4.5, aren’t you?), since the compiler will automatically synthesize properties for you. Handy, eh?

You next need to add the implementation for initWithText:, which initializes a SHCToDoItem instance with the supplied text. For convenience, also add the toDoItemWithText: class method, which makes object creation easier.

Add the following code to SHCToDoItem.m after the @implementation line:

-(id)initWithText:(NSString*)text {
    if (self = [super init]) {
      self.text = text;
    }
    return self;
}

+(id)toDoItemWithText:(NSString *)text {
    return [[SHCToDoItem alloc] initWithText:text];
}

Now that you have your to-do item class, creating an array of to-do items and displaying them in the UITableView is pretty standard stuff, so we’ll quickly rattle through the next few steps.

Add the following code to the top of SHCViewController.m (below the existing #import line, replacing the empty class extension and the @implementation line):

#import "SHCToDoItem.h"

@implementation SHCViewController {
    // an array of to-do items
    NSMutableArray* _toDoItems;
}

-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self)
    {
        // create a dummy to-do list
        _toDoItems = [[NSMutableArray alloc] init];
        [_toDoItems addObject:[SHCToDoItem toDoItemWithText:@"Feed the cat"]];
        [_toDoItems addObject:[SHCToDoItem toDoItemWithText:@"Buy eggs"]];
        [_toDoItems addObject:[SHCToDoItem toDoItemWithText:@"Pack bags for WWDC"]];
        [_toDoItems addObject:[SHCToDoItem toDoItemWithText:@"Rule the web"]];
        [_toDoItems addObject:[SHCToDoItem toDoItemWithText:@"Buy a new iPhone"]];
        [_toDoItems addObject:[SHCToDoItem toDoItemWithText:@"Find missing socks"]];
        [_toDoItems addObject:[SHCToDoItem toDoItemWithText:@"Write a new tutorial"]];
        [_toDoItems addObject:[SHCToDoItem toDoItemWithText:@"Master Objective-C"]];
        [_toDoItems addObject:[SHCToDoItem toDoItemWithText:@"Remember your wedding anniversary!"]];
        [_toDoItems addObject:[SHCToDoItem toDoItemWithText:@"Drink less beer"]];
        [_toDoItems addObject:[SHCToDoItem toDoItemWithText:@"Learn to draw"]];
        [_toDoItems addObject:[SHCToDoItem toDoItemWithText:@"Take the car to the garage"]];
        [_toDoItems addObject:[SHCToDoItem toDoItemWithText:@"Sell things on eBay"]];
        [_toDoItems addObject:[SHCToDoItem toDoItemWithText:@"Learn to juggle"]];
        [_toDoItems addObject:[SHCToDoItem toDoItemWithText:@"Give up"]];
    }
    return self;
}

Here you import the new to-do item class header, add an instance variable _toDoItems, and override initWithNibName:bundle: to populate this array with dummy data. The above code shows the value of the toDoItemWithText: class method, which has removed the need to repeatedly alloc/init the to-do objects.

You need to supply a datasource for the table view. For a simple application, it makes sense to use the view controller as the datasource, so go right ahead and edit SHCViewController.h to adopt the UITableViewDataSource protocol:

@interface SHCViewController : UIViewController <UITableViewDataSource>

You need to set the view controller as the datasource of the table view. Normally, you would do this via Interface Builder by connecting the datasource for the table view to the SHCViewController. But you can also do this via code by adding the following line to the end of viewDidLoad in SHCViewController.m:

self.tableView.dataSource = self;
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];

The above code also registers the UITableViewCell class as the class that will supply cells for the table view.

The UITableViewDataSource protocol defines two methods that must be implemented by any class that adopts the protocol. One (tableView:numberOfRowsInSection:) details the number of rows in each section, and the other (tableView:cellForRowAtIndexPath:) requests cell instances for a specific row/section.

The implementation of these methods is pretty simple – just add the code shown below to the end of SHCViewController.m (but before the closing @end):

#pragma mark - UITableViewDataSource protocol methods
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return _toDoItems.count;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *ident = @"cell";
    // re-use or create a cell
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ident forIndexPath:indexPath];
    // find the to-do item for this index
    int index = [indexPath row];
    SHCToDoItem *item = _toDoItems[index];
    // set the text
    cell.textLabel.text = item.text;
    return cell;
}

You only have a single section, which is the default behavior for the table view, so the above simply returns the number of items in response to the tableView:numberOfRowsInSection: message.

The implementation for tableView:cellForRowAtIndexPath: is mostly boilerplate code: a cell is created, the relevant to-do item is retrieved, and the text on the cell is set.

Note: In iOS versions before iOS 6, when creating a new UITableViewCell, you had to first dequeue the cell, and if you didn’t get a cell via the reusable pool, you had to create the cell explicitly via code. This is no longer necessary in iOS 6, since dequeueReusableCellWithIdentifier:forIndexPath: automatically creates a new cell for you if one isn’t available via the reuse pool, as long as you have a class registered for the cell identifier beforehand. (That’s what you did in viewDidLoad above.)

Build and run your code, and you will be presented with the wonderfully minimalist to-do list shown below:

Colin Eberhardt

Contributors

Colin Eberhardt

Author

Over 300 content creators. Join our team.