IMG_4589I’ve been playing with my iPhone a bit lately. Trying to work out an application that I think will be useful (more on that some other time). In the mean time, I wanted to let the user of the application flick between views to see different detailed view screens. Each flick should take the user either forward or back in the list. The transitions should push the new item into view and have a little bounce on the end.

Turns out, this wasn’t trivial. I’m not sure now why I thought it would be trivial. Maybe because I’ve seen it in a few apps.

Anyway, what follows is how I did it. Maybe there is an easier way, if so, let me know, but this appears to work.

I’m going to create a simple Core Data backed, Navigation-based iPhone application. There will be two screens, the main list screen and a detailed view. The user will be able to flick between detailed views. Note, there is no real error checking, and possibly memory leaks in this code. You’ll need to fix up as appropriate for your application.

Firing up XCode, create a new project. Under the iPhone OS Applications template section, you’ll want to select Navigation-based Application. Make sure you’ve got Use Core Data for storage selected as well. In my case I’m naming the application Flick. choose_template

With the application created you should be able to execute it in the simulator and add a series of timestamps to the main view.

managd_object_class Before we start adding screens, let’s create a class to hold our model entity. Double click on the Flick.xcdatamodel file to open the data modeler. Click on the Event entity and press command-n. Select, in the Mac OS X Cocoa section, Managed Object Class. Click through to finish the creation as the rest of the defaults will suffice. You should now have Event.h and Event.m files created in your project. create view controller

With the base application in place we still need to create a screen to allow viewing of our events. I’m backing this screen with a UITableViewController because that’s how my application does it. You could probably use a plain UIViewController if you don’t need to extra functionality of the table view controller.

Add a new file to the project and, under iPhone OS Cocoa Touch Class, select Objective-C class and set a Subclass of UITableViewController. Name the file EventViewController.m and have the .h file auto generated.

Opening up RootViewController.m we need to hookup the code to create our new view when one of the rows is selected.

First, lets mark the rows as having another view. Add cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; into the tableView:cellForRowAtIndexPath: method right after the UITableViewCell is created.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    Event *event = (Event *)[fetchedResultsController objectAtIndexPath:indexPath];

    EventViewController *controller = [[EventViewController alloc] initWithStyle:UITableViewStyleGrouped];
    controller.event = event;

    [self.navigationController pushViewController:controller animated:YES];
    [controller release];

Using the tableView:didSelectRowAtIndexPath: delegate method we retrieve the selected event from our fetchedResultsController. We then create a new EventViewController with a table style of UITableViewStyleGrouped There are two styles to tables Grouped and Plain.

A plain table view. Any section headers or footers are displayed as inline separators and float when the table view is scrolled.
A table view whose sections present distinct groups of rows. The section headers and footers do not float.

With the controller created we assign the selected event to the controller. Finally, we push the new view controller into the navigation stack using [self.navigationController pushViewController:controller animated:YES]. This will set our new view as the view to be displayed. Oh, and don’t forget to import EventViewController.h and Event.h.

We want our new view controller to have a back button. If you don’t set the name in the MainWindow.xib file the back button will be available, but invisible. Double click the MainWindow.xib file to open it in Interface Builder. Click on the navigation bar in the view and press shift-command-i to open the inspector. Press command-1 to switch to the attributes panel. In the Back Button field enter Events. You can now save and exit Interface Builder. Thanks to Owen, setting self.navigationItem.title = @"Events" in the viewDidLoad method of RootViewController will set the correct text into the back button.

Continuing on, we can fill in the EventViewController files, starting with the header.

#import "Event.h"

@interface EventViewController : UITableViewController {
    Event *event;
@property (nonatomic, retain) Event *event;

The view controller has one attribute, the event that was selected. We setup a property for the event to handle the getters and setters for us.

#import "EventViewController.h"

@implementation EventViewController

@synthesize event;

- (void)viewDidLoad {
    self.navigationItem.title = @"Event Info";

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 1;

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *LocationCellIdentifier = @"Timestamp";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:LocationCellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
                                       reuseIdentifier:LocationCellIdentifier] autorelease];
        cell.accessoryType = UITableViewCellAccessoryNone;

    NSDateFormatter *dateFormat = [[[NSDateFormatter alloc] init] autorelease];
    [dateFormat setDateStyle:NSDateFormatterLongStyle];
    [dateFormat setTimeStyle:NSDateFormatterLongStyle];

    cell.textLabel.text = [dateFormat stringFromDate:event.timeStamp];

    return cell;

- (void)dealloc {
    [super dealloc];

Next up is the implementation file. When the view finishes loading we want to set the title to display Event Info. This is done in viewDidLoad by calling self.navigationItem.title = @"Event Info".

We’re only going to show one thing in our table, the timestamp, so we setup the table to have a single section and a single row. We then create a cell to display the time stamp.

You should be able to build and run the application at this point. You can click on the events to move to the subview. From there you can navigate back to the main list.

With that, we’re ready to add some flicking to our application.

There is no flick event built into the iPhone APIs that I could find. The way to achieve a flick is to listen for the touchesEnded:withEvent: delegate method and determine if a flicked happened. The one catch, this delegate is available on UIView objects. We don’t, currently, have a UIView object we can attach the delegate method too.

So, we’re going to subclass UIView deal with the flicking, then let our parent EventViewController know when the flick happened so we can switch views. Sound good? Ok, let’s go.

Add a new class to the project. Under iPhone OS Cocoa Touch Class select Object-C class. Make sure UIView is selected in the drop down. Name the new file EventView.m.

@protocol EventViewDelegate;

@interface EventView : UITableView {
    id <UITableViewDelegate, EventViewDelegate> delegate;
@property (nonatomic, assign) id <UITableViewDelegate, EventViewDelegate> delegate;

@protocol EventViewDelegate <NSObject>
- (void)eventView:(EventView *)view flickDirection:(int)dir;

There are a couple things going on here. First, I switched the inherited class to UITableView as that’s what we’re actually dealing with.

We add one attribute to the EventView class. We need to be able to assign the controller as a delegate for our view so we can notify the controller of the flick. In order for the table to continue working, we also need to make sure we maintain the UITableViewDelegate protocol already attached to the delegate object in the super class.

Our protocol is pretty simple, we need one method implemented, eventView:flickDirection:. This will be called when the view is flicked and set the flickDirection to -1 for a left flick and 1 for a right flick.

#import "EventView.h"


@implementation EventView
@synthesize delegate;

- (int)flicked:(UITouch *)touch {
    CGPoint start = [touch previousLocationInView:self];
    CGPoint end = [touch locationInView:self];

    double x = end.x - start.x;
    double y = end.y - start.y;
    double dist = sqrt((x * x) + (y * y));

    if (dist > MIN_FLICK_DISTANCE) {
        if (end.x < start.x) return 1;
        else return -1;
    return 0;

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    int dir = [self flicked:touch];

    if (dir != 0) [self.delegate eventView:self flickDirection:dir];

    [super touchesEnded:touches withEvent:event];

- (void)dealloc {
    [super dealloc];

With the header out of the way we can implement the class. The touchesEnded:withEvent: method will be executed when the touch is complete. We can access the touch event, using [touches anyObject] and look at the [touch previousLocationInView:self] and [touch locationInView:self] methods to access the two touch points.

If these points are more then MAX_FLICK_DISTANCE apart then we have a flick. As long as we have a flick we notify our delegate by calling the eventView:flickDirection: method we defined in our protocol.

With the view created, we can hook it up to our EventViewController. Open up EventViewController.h. We need to add an import for the EventView.h file and then add the EventViewDelegate protocol to the view controller.

    @interface EventViewController : UITableViewController <EventViewDelegate>

Moving over to EventViewController.h we need to add an import for the RootViewController.h. We’ll be accessing it’s fetched results in order to get the new view information.

We need to load our new EventView as the view to use for the controller. We do this by overriding loadView

- (void)loadView
    EventView *eventView = [[[EventView alloc] initWithFrame:CGRectZero
                                                       style:UITableViewStyleGrouped] autorelease];
    [eventView setDelegate:self];
    [eventView setDataSource:self];

    self.view = eventView;
    self.tableView = eventView;

Nothing too special here, we create our EventView and tell it that the current controller is the delegate and data source for the view. Finally, we assign the new view to the controllers view and tableView methods. You need to assign both of these for things to work properly.

The last step is to implement the eventView:flickDirection: method.

- (void)eventView:(EventView *)view flickDirection:(int)dir
    NSFetchedResultsController *frc = [[[self.navigationController viewControllers] objectAtIndex:0] fetchedResultsController];

    NSIndexPath *path = [frc indexPathForObject:[self event]];
    NSInteger row = [path row];

    if ((dir == -1) && (row > 0)) row --;
    else if ((dir == 1) && (row < ([[frc fetchedObjects] count] - 1))) row ++;

    if (row == [path row]) return;

    NSIndexPath *newPath = [NSIndexPath indexPathForRow:row inSection:[path section]];
    [self setEvent:[frc objectAtIndexPath:newPath]];

    [self.tableView reloadData];

The idea here is simple, grab the fetched results from the root view using [[[self.navigationController viewControllers] objectAtIndex:0] fetchedResultsController]. The root view is always at the bottom of the view stack. The fetched results controller can then be used to find the index for the event that we’re currently viewing. We can then get the next view, either left or right based on flick direction, to be displayed. We set that event into the view and reload the table data.

Building and running you should be able to flick between views. Although, there is an issue, the visuals really suck. Let’s do something about that.

In order to make things look good we should push the new views into place and, if we’re on the end view, bounce a little to show the user they can’t go any further.

For this, we’re going to dip into a little but of Core Animation. To that end, we need to import <QuartzCore/QuartzCore.h> into the EventViewController.h file and add the QuartzCore.framework into our project.

We’ll start with a little helper method.

- (void)addAnimation:(NSString *)name subtype:(NSString *)subtype duration:(float)duration
               start:(float)start end:(float)end delegate:(id)animDelegate {
    CATransition *anim = [CATransition animation];

    [anim setDuration:duration];
    [anim setType:kCATransitionPush];
    [anim setSubtype:subtype];
    [anim setStartProgress:start];
    [anim setEndProgress:end];
    [anim setDelegate:animDelegate];
    [anim setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];

    [[[self.view superview] layer] addAnimation:anim forKey:name];

addAnimation:subtype:duration:start:end:delegate: adds a new animation to the system. We’re always using push animations and always using the same ease timing function. Otherwise, we can set things up as we need.

To start, add the following to the end of eventView:flickDirection:

[self addAnimation:@"SwitchView"
               subtype:(dir == -1 ? kCATransitionFromLeft : kCATransitionFromRight)

If you run this, you should be able to flick between views with a nice push animation. You can change the duration to make the transition slower or faster as desired.

Now, let’s add the bounce when we’re at the end of our data. Inside eventView:flickDirection: change if (row == [path row]) return; to the following.

if (row == [path row]) {
  [self addAnimation:@"PushHalfOut"
             subtype:(dir == -1 ? kCATransitionFromLeft : kCATransitionFromRight)

You’ll notice we set the delegate in this instance to ourselves. This will cause animationDidStop:finished: to be called when the animation is finished. We’re going to add that callback next.

- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag {
    [self addAnimation:@"PushHalfIn"
               subtype:[(CATransition *)animation subtype]

With the first animation we’re shifting the view off screen by a quarter of the screen. Then, when that animation is finished, we trigger a second animation to shift the view back on screen again. Giving the appearance of a slight bounce.

With that, we have our views flicking and bouncing. You can grab the full source to this tutorial at