iOS 4 & iOS 5 : Custom UIAppearance Tutorial

Having battled against the iOS API to theme my applications I was extremely pleased when iOS 5 introduced UIAppearance, allowing individual user interface elements or entire classes of elements to be themed in a particular way. Recently one of our apps required support for iOS 4.3 so I had to go back to my previous (more painful) method. I thought it might be useful to document both these methods to help anyone who might face a similar pain so hope this helps.

iOS5
In this example I am going to focus on theming a UINavigationBar but the principles in this article are the same for other elements. First lets look at how to change the background image of an individual UINavigationBar,

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"nav bar.png"] forBarMetrics:UIBarMetricsDefault];
}

However, if you want to have the same background image on every UINavigationBar within the app this can be problematic, you will have to set the background image in several places depending on where you create all of your UINavigationBars. Instead you can use the UIAppearance of the UINavigationBar.

[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@"nav bar.png"] forBarMetrics:UIBarMetricsDefault];

This will theme every UINavigationBar element with the nav bar image, and the best thing is that in iOS 5 every UI component (or at least all of those I have used) implement UIAppearance so you can use this technique to theme all of your UIBarButtons, UIToolbars or any set of components you need to.

For most of us this will be the way we theme things from now on however, for some of us we still feel the obligation (or need) to support older versions (at least in the short term) so here is how to achieve the same results in iOS 4.3.

iOS 4

Objective-C categories, allows you to add additional functionality to objects that you are not the original creator of. You don’t even need the source to the original classes. Using this method you can override the drawRect method and customise the appearance of all of the UINavigationBars.

@implementation UINavigationBar (UINavigationBarCategory)
 
- (void)drawRect:(CGRect)rect {
    UIColor *color = [UIColor blackColor];
    UIImage *img  = [UIImage imageNamed: @"nav bar.png"];
    [img drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
    self.tintColor = color;
}
 
@end

Using Objective-C categories can be a useful and convent way of theming UINavigationBars and UIToolbars etc however, they do alter the appearance of all instances of this so it can throw off the appearance of other UI components that are part of the SDK. For example I recently looked to change the background of a UITableView with a category which worked well after I set the UILabels on the UITableViewCells background colour to clear. However, when I launched the UIImagePickerController the background was also changed on this and I had no easy way of setting the UILabels in its cells to have a clear background so the view looked horendus. Instead of using categories I created a base UITableView class that I extend to ensure that each UITableView is themed correctly.

Sources
This is a really useful resource that I drew from to theme my user interfaces in iOS4
Ray Wenderlich: iOS 5 User Interface Customisation

Teaching Style: David Foster Wallace’s Syllabus

Earlier today John Gruber posted an interesting link on Daring Fireball to an article by Katie Roiphe on David Foster Wallace and his teaching style that really resonated with me. Before I expand on my thoughts here is what was posted.

David Foster Wallace’s Syllabus
“If you are used to whipping off papers the night before they’re due, running them quickly through the computer’s Spellchecker, handing them in full of high-school errors and sentences that make no sense and having the professor accept them ‘because the ideas are good’ or something, please be informed that I draw no distinction between the quality of one’s ideas and the quality of those ideas’ verbal expression, and I will not accept sloppy, rough-draftish, or semiliterate college writing. Again, I am absolutely not kidding.”

Katie Roiphe, writing for Slate:

Of course, this is not the part of teaching that most people pour their hearts into. It’s just a syllabus! Wallace is bringing to the endeavor rigorous Salingerish standards of not lying, or not being phony, that would reproach other more ordinary people if these standards did not border on parody, and were not expressed in such a good natured and honorable way.

with John Gruber adding,

“Don’t just go through the motions. Don’t accept dogma. Look for ways that you might be wrong, don’t look for ways to prove you’re right. Think. Express your thoughts with as much precision and care as you can muster.

That’s why Wallace’s work serves as a beacon, a yardstick, for my own.”

My Thoughts
Having lectured extensively myself this is such a refreshing take on teaching. Learning has become extremely prescriptive and is generally delivered in the same mundane way. Individuals are as much to blame as the organisational establishments they work within, wether they be schools, universities or corporate environments. A focus on, exams or league tables as a measure of success has become the norm. However, many success stories are lost with this metric. This may seem like a strange interjection this early on but stay with me for just a second. During John Gruber’s keynote at Singleton he made reference to the following Stanley Kubrick quote to define the difference between Apple and Microsoft,

“Sometimes the truth of a thing is not so much in the think of it, as in the feel of it.”

In essence he was describing how we should consider these companies and the underlying truth engrained in their products and corporate culture. With Microsoft products he suggests that the truth of them is in “the think” of them, to understand the true nature of a Microsoft product you can lay it out as a bullet point list highlighting features etc. With Apple products the truth lies in “the feel”, and you can no better explain why you feel affection for your iPhone/iPad or aspects of the user experience in the same way you cannot explain why you feel affection to the people you love, it is very emotional.

In todays learning establishments we are taking the Microsoft approach to measuring the success of our students when instead we should be taking the Apple approach. Often the choice of the metrics that we measure our students learning by is not based on what is best for the student or students, instead the focus remains on what is easiest for our teachers; which is a very sad indictment of the situation we find ourselves in. While I agree with David’s approach to engaging and encouraging the creative process I think he could go yet further. In his Syllabus he “draw[s] no distinction between the quality of one’s ideas and the quality of those ideas’ verbal expression, and [he] will not accept sloppy, rough-draftish, or semiliterate college writing. I would suggest that this expression of idea should not be constrained to writing. The ability to express ideas differs dramatically between individuals, with each preferring different styles, verbal, written, graphical, kinaesthetic etc. While more difficult to assess this is something we should strive toward in every Syllabus for learning we ever put together.

One final thought that continously gets me down particularly when watching other lecturers is the arrogance that comes with the position. Many teachers have either forgotten or do not believe that our students have as much to teach us, as we have to teach them, the learning process is a two way streak. All we can claim to bring to the party is experience, that we hope to pass on.

iOS 5 Storyboards: A Practical Example

My Tasks
After a conversation I had on stack overflow I realised that, while my previous tutorial on storyboards has been very useful to many in the community, a real world example was what was really required to help people build a deeper understanding of the concept.

This tutorial is a simple one focused around a task list, with a simple LAMP backend. For this tutorial I will assume that you have Php, MySql and whichever server technology you wish to use – I use Apache. You can download the source for MyTasks here and the database sql schema from here

Step 1: My Tasks iOS 5 client using Storyboards
Create a new Single View Applicaition project in Xcode. Name it MyTasks and make sure you have Storyboard and ARC checked. The default Scene created when you create the project extends UIViewController and we want a UITableViewController for this application. The quickest way to do this is just to delete the default UIViewController class and scene, created a new UITableViewController class called SSTableViewController and pull on a new UITableView onto your Storyboard, creating a new scene, and set its class to SSTableViewController in the Identity Inspector.

To communicate the with server we use HTTP Post. For speed you should download Mr Gando’s SimplePost and drag this into you project. SimplePost creates multi-part NSMutableRequests that you can use to post dictionaries of data to your web service – importantly it works with ARC. To complete the scaffolding for this Xcode project create a Task class for representing your tasks.

.h
 
#import <Foundation/Foundation.h>
 
@interface Task : NSObject
{
    NSInteger taskID;
    NSString *name;
}
@property(assign,nonatomic)NSInteger taskID;
@property(strong,nonatomic)NSString *name;
 
-(id)initWithDictionary:(NSDictionary *)dict;
-(id)initWithName:(NSString *)aName;
-(NSDictionary *)toDictionary;
@end

Create a constructor that can build an instance of the class from the dictionary returned from the web service. Also create a toDictionary method to serialise the objected so that it can be posted to the web service.

.m
#import "Task.h"
 
@implementation Task
 
@synthesize name,taskID;
 
-(id)init{
    return [self initWithName:nil ];
}
 
-(id)initWithDictionary:(NSDictionary *)dict{
    if(self!=nil){
        self.taskID = [[dict objectForKey:@"taskID"] integerValue];
        self.name = [dict objectForKey:@"name"];
    }
    return self;
}
 
-(id)initWithName:(NSString *)aName{
    if(self!=nil){
        NSDate *date = [NSDate date];       
        self.taskID = [date hash];
        self.name = aName;
    }
    return self;
}
 
-(NSDictionary *)toDictionary{
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    [dict setObject:self.name forKey:@"name"];
    [dict setObject:[[NSNumber numberWithInteger:self.taskID] stringValue] forKey:@"taskID"];
    return dict;
}
@end

Now lets look at the key to this tutorial, the Storyboard. From our previous setup we have a UITableViewController for displaying a list of tasks so an important feature will be for people to be able to add a task. So we will add a plus button to the initial scene in our storyboard so that we can support users in creating new tasks. To add this button we are going to embed the SSTableViewController in a UINavigationController by selecting the UITableViewController and going to the menu and selecting Editor->Embed In-> Navigation Controller. Then drag on a UIBarButtonItem on to the top right hand side of the navigation bar, in the attribute inspector change the identifier of the UIBarButtonItem to Add.

You will notice that by embedding your UITableViewController in a UINavigationController a new scene is added to the storyboard and a relationship between the two is created. The first scene is the navigation controller and the link is the relationship between that and the table view. Now lets create a new task scene. Start by dragging a new UIViewController onto your Storyboard. We want to have a cancel button incase a user changes their mind and does not want to add a new task and a done button to confirm they have finished creating the new task. So, once again, we embed this UIViewController in a UINavigationController and drag on two UIBarButtonItems, cancel and done onto it.

Now we want to display the new task UIViewController when the Add button is pressed so we right click on the UIBarButtonItem and drag across to the UINavigationController of our new UIViewController and choose modal from the resulting popup. You have now successfully created a Segue. If you run the application and tap the Add button the new task scene will be presented modally. Finally add a label and a textfield to the new task view so that users can add a name for the new task.

We now need to create a class for this New Task UIViewController (NewTaskViewController). This must implement the UITextFieldDelegate to enable us to handle any edits and contain an outlet for the text field for capturing the text being created for the task name. We are going to use the delegation pattern to allow another controller to decide how to handle a New Task canceled event and New Task created event so we must add a protocol and a delegate that implements this protocol.

.h
#import <UIKit/UIKit.h>
#import "Task.h"
 
@protocol NewTaskDelegate;
 
@interface NewTaskViewController : UIViewController <UITextFieldDelegate>
{
    id<NewTaskDelegate>delegate;
    UITextField *taskNameField;
}
 
@property(strong,nonatomic)id<NewTaskDelegate>delegate;
@property(strong,nonatomic)IBOutlet UITextField *taskNameField;
 
-(IBAction)cancel:(id)sender;
-(IBAction)done:(id)sender;
 
@end
 
@protocol NewTaskDelegate <NSObject>
 
-(void)newTaskViewController:(NewTaskViewController *)ntvc didCreateNewTask:(Task *)task;
-(void)cancelNewTaskViewController:(NewTaskViewController *)ntvc;
 
@end
.m
#import "NewTaskViewController.h"
 
@implementation NewTaskViewController
 
@synthesize delegate,taskNameField;
 
- (void)didReceiveMemoryWarning
{
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];  
    // Release any cached data, images, etc that aren't in use.
}
 
#pragma mark - Actions
-(IBAction)cancel:(id)sender{
    [self.delegate cancelNewTaskViewController:self];
}
 
-(IBAction)done:(id)sender{
    if([self.taskNameField.text length]<=0)
    {
        NSLog(@"You have not entered a name for this task %@",self.taskNameField.text);
        return;
    }
    Task *newTask = [[Task alloc] initWithName:taskNameField.text];  
    [self.delegate newTaskViewController:self didCreateNewTask:newTask];
}
 
#pragma mark - TextFieldDelegate
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    return YES;
}
 
#pragma mark - View lifecycle
- (void)viewDidLoad
{
    [super viewDidLoad];
 
    // Uncomment the following line to preserve selection between presentations.
    // self.clearsSelectionOnViewWillAppear = NO;
 
    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
 
- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}
 
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
}
 
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
}
 
- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
}
 
- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
}
 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
	return YES;
}
@end

Your main UITableViewController (SSTableViewController) must implement the NewTaskDelegate protocol so that it can receive the notifications of new tasks created or canceled. Once we have done so we must then set it as the delegate to the NewTaskViewController, this is done in the prepareforSegue method. In this method you must first identify the Segue that has been activated by looking at its identifier. To do this you must set its identifier by selecting the Segue in your storyboard and in the attribute inspector change its name to NewTask.

Now implement the prepareForSegue method in the SSTableViewController using the identifier to ensure you are setting things up on the correct Segue. For the NewTask Segue we know that the UIViewController being presented is a UINavigationController so we have to get that using the segue’s destinationViewController method and then pull the topViewController from that to get the NewTaskViewController where we can then set ourself as its delegate, like so.

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    if([segue.identifier isEqualToString:@"NewTask"]){
        UINavigationController *nv = (UINavigationController *)[segue destinationViewController];
        NewTaskViewController *ntvc = (NewTaskViewController *)nv.topViewController;
        ntvc.delegate = self;
    }
}

The final piece of the jigsaw is to finish the implementation of the SSTableViewController to retrieve the tasks pending and updating of completed tasks.

.h
 
@interface SSViewController : UITableViewController <NewTaskDelegate>
{
    NSArray *pendingTasks;
}
 
@property(strong,nonatomic) NSArray *pendingTasks;
-(void)addTask:(Task *)task;
-(void)refreshPendingTasks;
-(void)setTaskCompleted:(Task *)task;
@synthesize pendingTasks;
 
static NSString *kPendingTasks = @"http://localhost/~scott/my_tasks/pending_tasks.php";
static NSString *kSetTaskCompleted = @"http://localhost/~scott/my_tasks/set_task_completed.php";
static NSString *kAddTask = @"http://localhost/~scott/my_tasks/add_task.php";
 
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Release any cached data, images, etc that aren't in use.
}
 
#pragma mark - NewTaskDelegate
 
-(void)newTaskViewController:(NewTaskViewController *)ntvc didCreateNewTask:(Task *)task{
    [self.navigationController dismissModalViewControllerAnimated:YES];
    [self addTask:task];
}
 
-(void)cancelNewTaskViewController:(NewTaskViewController *)ntvc{
    [self.navigationController dismissModalViewControllerAnimated:YES];
}
 
#pragma mark - NSURLConnectionDelegate
 
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
        NSArray *array = (NSArray *)[NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListImmutable format:0 errorDescription:nil];
        NSMutableArray *ma = [NSMutableArray array];
        for(int i=0;i<[array count];i++){
            NSDictionary *d = (NSDictionary *)[array objectAtIndex:i];
            Task *task = [[Task alloc] initWithDictionary:d];
            [ma addObject:task];
        }
        self.pendingTasks = ma;
        [self.tableView reloadData];    
}
 
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"did fail");
}
 
#pragma mark -Server Actions
 
-(void)addTask:(Task *)task{
    NSMutableURLRequest *request = [SimplePost urlencodedRequestWithURL:[NSURL URLWithString:kAddTask] andDataDictionary:[task toDictionary]];
    [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
 
-(void)setTaskCompleted:(Task *)task{
    NSMutableURLRequest *request = [SimplePost urlencodedRequestWithURL:[NSURL URLWithString:kSetTaskCompleted] andDataDictionary:[task toDictionary]];
    [[NSURLConnection alloc] initWithRequest:request delegate:self];
}

Step 2: Communications
This section relies on a modified version of jsjohnst’s plist parser. The modified plist parser is available in the project bundle.

Three process are required from the server,

  • Add a Task (add_task.php)
  • Retreive Pending Tasks (pending_tasks.php)
  • Mark task as Complete (set_task_completed.php)

To add a task we must pass the task from the iOS client to the server. In this example the client itself ensures that each new task has a unique identifier by using the date and time that it is created, however for a more robust solution you may want to have this done on the server to ensure that every task is unique, particular if you have multiple users.

After receiving the taskID and name we call the addTask method defined in our functions file and request the pending tasks and pass them back to the client. This will result in the recently added task being added to the list and send back with all the pending tasks. Again this could be refined and the new task might be created and stored on the client once a confirmation has been received that the server has been successfully updated rather than downloading the entire list again.

Add a Task

<?php
 
require_once('PlistParser.php');
require_once('functions.php');
 
$taskID = $_POST['taskID'];
$name = $_POST['name'];
 
addTask($taskID,$name);
$pending_tasks = getPendingTasks();
 
$pp = new PlistParser();
$plist = $pp->convertIntoPlist($pending_tasks,true);
echo $plist;

Before moving on it is important to note the use of the PlistParser. This takes an array of tasks (in this case the tasks are represented as dictionaries returned by the getPendingTasks) and constructs a plist which is extremely simple to parse on the iOS client as we will see a little later. The pending_tasks.php and set_task_completed.php are very similar in structure but they call on different methods in the functions.php file. Lets take a little look at the functions file and the addTask, getPendingTasks and taskCompleted methods.

function addTask($taskID,$name){
     $link = mysql_connect('<server>', '<your username>', '<your password>');
    if (!$link) {
        die('Could not connect: ' . mysql_error());
    }
    mysql_select_db("my_tasks");
    $query = "INSERT INTO tasks (taskID, name, complete) VALUES ('$taskID', '$name', 'false')";    
    mysql_query($query);
    mysql_close($link);
}

This method connects to the database using mysql_connect, you must change the connection credentials to suit your setup. If the connection fails the method dies presenting an error otherwise it connects to the selected database. If you have named your database as specified previously this will be my_tasks. Finally this method takes the parameters passed and inserts them into the database closing the connection. The getPendingTasks method and the taskCompleted methods are very similar in structure the main difference is the database query.

All Pending Tasks

function getPendingTasks(){
    $pending_tasks = array();
 
    $link = mysql_connect('<server>', '<username>', '<password>');
    if (!$link) {
        die('Could not connect: ' . mysql_error());
    }
    mysql_select_db("my_tasks");
    $query = "SELECT taskID, name, complete FROM tasks WHERE complete=false";
    $result = mysql_query($query);
 
    while ($row = mysql_fetch_assoc($result)) {
        $task = array();
        $task["taskID"] = $row['taskID']; 
        $task["name"] = $row['name'];
        array_push($pending_tasks,$task);
    }
    mysql_close($link);
    return $pending_tasks;
}

Task Completed
Finally to mark a task as complete the method is as follows

function taskCompleted($taskID){
     $link = mysql_connect('<server>', '<username>', '<password>');
    if (!$link) {
        die('Could not connect: ' . mysql_error());
    }
    mysql_select_db("my_tasks");
    $query = "UPDATE tasks SET complete=true WHERE taskID='".$taskID."'";    
    mysql_query($query);
    mysql_close($link);
}

Step 3: Creating a MySQL Database
There are various ways to create a MySQL database, in this tutorial we will be using a database management tool called Seguel Pro (I highly recommend it, it is a really good tool). Should you want to load the database in your own tool you can get the sql schema from here, or you can quickly construct it yourself.

To construct the simple task database we have to create a table with three columns to store the task identifier, the task and its current status (completed or pending). This is done by following these steps,

  1. Create a new database, (Database->Add Database…),and enter my_tasks as the database name
  2. Add a new table to the database called tasks
  3. Add three columns, taskID (int), name (varchar 255), complete (tinyint)

This is an extremely simple database intended to be used to illustrate how to retrieve/update values via the iOS client. The next stage is to build the Php scripts that will enable the iOS client to interact with the database.

This completes the tutorial all of the source files can be downloaded from here allowing you to set up your won simple task server and using the iOS client to interact with this simple backend, I hope this helps.

Concept Artwork: The Incredibles

Was struggling to find inspiration this week, my motto is if in doubt think about pixar. I was quite pleased with the outcome this might become a series to capture all of the pixar movies, who knows I guess it depends on how long it takes me to find something to get my teeth into.

iOS 5: Drag and Drop Between UITableViews

On an iPad esspecially, it is often the case that you may have multiple UITableViews on the screen at the one time, unlike on the iPhone. I wanted to have two UITableView that I could drag and drop cells between the two to swap items between a set of all possible choices and a set of selected choices.

It made sense to me to use a long press to pop off a cell that can then be dragged across the screen and dropped on another view. I took inspiration from this YouTube Video however after looking at the the code on git hub I really wanted to work on trying to remove the need for an overlay view to capture the touches as it felt a little hacky to me (although kudos for the solution). So if you want to see what it looks like then you can check out the final demo running on YouTube and the following illustrates how I achieved it.

Creating DragAndDropTableViewController
The key to this method is recognising that the UILongPressGestureRecognizer is a continuous event that even after it has been fired your receiver will get UIGestureRecognizerStateChanged events that correspond to where your finger is after the long press has been performed. This is important to note since it lets you know where your finger is on the screen after the long press has begun allowing you to detect dragging.

First I added a long press gesture to the UITableViewCell that is to be dragged and dropped.

UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:cell action:nil];
longPress.delegate = self;
[cell addGestureRecognizer:longPress];

Then, as always with gestures we must implement the UIGestureRecognizerDelegate for more on Gestures you can check out my how to tutorial here or have a look at the video for this tutorial on YouTube.

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
    if([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]){
        [gestureRecognizer addTarget:self action:@selector(longGestureAction:)];
    }
    return YES;
}
 
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
    return YES;
}
 
-(void)longGestureAction:(UILongPressGestureRecognizer *)gesture{
    UITableViewCell *cell= (UITableViewCell *)[gesture view];
 
    switch ([gesture state]) {
        case UIGestureRecognizerStateBegan:{          
            NSIndexPath *ip = [self.tableView indexPathForCell:cell];
            [self.tableView setScrollEnabled:NO];
            if(ip!=nil){
                [self.draggableDelegate dragAndDropTableViewController:self  draggingGestureWillBegin:gesture forCell:cell];
                UIView *draggedView = [self.draggableDelegate dragAndDropTableViewControllerView:self ];
                //switch the view the gesture is associated with this will allow the dragged view to continue on where the cell leaves off from
                [draggedView addGestureRecognizer:[[cell gestureRecognizers]objectAtIndex:0]]; 
                [self.draggableDelegate dragAndDropTableViewController:self draggingGestureDidBegin:gesture forCell:cell];
            }
        }
            break;
        case UIGestureRecognizerStateChanged:{
            [self.draggableDelegate dragAndDropTableViewController:self draggingGestureDidMove:gesture];
        }
            break;
        case UIGestureRecognizerStateEnded:{
            UIView *draggedView = [self.draggableDelegate dragAndDropTableViewControllerView:self];
            if(draggedView==nil)
                return;
 
            //this does not seem like the best way to do this yet you really don't want to fire one after the other I don't think
            [self.draggableDelegate dragAndDropTableViewController:self draggingGestureDidEnd:gesture];
            [self.dropableDelegate dragAndDropTableViewController:self droppedGesture:gesture];           
 
            [self.tableView setScrollEnabled:YES];
            [self.tableView reloadData];
        }
            break;
 
//        case UIGestureRecognizerStateCancelled:
//        case UIGestureRecognizerStateFailed:
//        case UIGestureRecognizerStatePossible:
//            [self.dragAndDropDelegate dragAndDropTableViewController:self draggingGesture:gesture endedForItem:nil];
            break;
        default:
            break;
    }
}

I added this UILongPressGestureRecognizer into a superclass called DragAndDropTableViewController as the handler itself does not need to be exposed and while it is quite simple it is not the prettiest but it handles some of edge cases that allow this to work and uses delegation to leave the details up to you. Lets have a look and the handler in more detail, first when the long press gesture begins we grab the view that has been pressed (which we know is a cell) and find the NSindexPath for it in our UITableView. We then fire off a method to the delegate to let it know that the dragging has begun, this is where the delegate (in my example) stores the item that the cell represents and creates the draggable representation however, I will get to that soon. The you grab the view from the delegate that will be the draggable representation, now for the key part you have to take the gesture from the cell and pass it over to the draggable representation. This means that the draggable view will continue to receive the touch gestures for the current instance of the recogniser giving the seamless dragging experience. I disable the scrolling of the tableView otherwise the table scrolls as you move your dragged item around so the quick fix was just to disable this.

UITableViewCell *cell= (UITableViewCell *)[gesture view];
 
        case UIGestureRecognizerStateBegan:{          
            NSIndexPath *ip = [self.tableView indexPathForCell:cell];
            [self.tableView setScrollEnabled:NO];
            if(ip!=nil){
                [self.draggableDelegate dragAndDropTableViewController:self  draggingGestureWillBegin:gesture forCell:cell];
                UIView *draggedView = [self.draggableDelegate dragAndDropTableViewControllerView:self ];
                //switch the view the gesture is associated with this will allow the dragged view to continue on where the cell leaves off from
                [draggedView addGestureRecognizer:[[cell gestureRecognizers]objectAtIndex:0]]; 
                [self.draggableDelegate dragAndDropTableViewController:self draggingGestureDidBegin:gesture forCell:cell];
            }
        }

Now that we have attached the gesture recogniser to the new view if we delete the cell in the draggingGestureDidBegin in the delegate it will not be released and will keep firing so now we can pass on the UIGestureRecognizerStateChanged to the delegate allowing it to reposition the dragging view as the touch moves around the screen.

        case UIGestureRecognizerStateChanged:{
            [self.draggableDelegate dragAndDropTableViewController:self draggingGestureDidMove:gesture];
        }

Finally notify the delegate that the item has been dropped and that the dragging has ended. At this point the delegate should look at the view that the item has been dropped on and take appropriate action. You also have to remember to re-enable the scrolling so the tableView act as normal once dragging ends.

        case UIGestureRecognizerStateEnded:{
            UIView *draggedView = [self.draggableDelegate dragAndDropTableViewControllerView:self];
            if(draggedView==nil)
                return;
 
            //this does not seem like the best way to do this yet you really don't want to fire one after the other I don't think
            [self.draggableDelegate dragAndDropTableViewController:self draggingGestureDidEnd:gesture];
            [self.dropableDelegate dragAndDropTableViewController:self droppedGesture:gesture];           
 
            [self.tableView setScrollEnabled:YES];
            [self.tableView reloadData];
        }

Using DragAndDropTableViewController
As you have seen the DragAndDropTableViewController is relatively simple and you can use it very simply should you want to achieve the dragging and dropping of cells between tables By implementing the DraggableDelegate and DroppableDelegate. First you must implement draggingGestureDidBegin and decide how to handle the dragging of a cell. In the draggingGestureWillBegin I take the cell that has been selected and take a snap shot of it. Note: To grab the snapshot of the CALayer you need to add the QuartzCore framework and include it, I add it to the pre-compiled header (.pch file). Then I construct a view to drag with a UIView and a UIImageView subview which displays the snap shot of the cell.

-(void)dragAndDropTableViewController:(DragAndDropTableViewController *)ddtvc draggingGestureWillBegin:(UIGestureRecognizer *)gesture forCell:(UITableViewCell *)cell{
 
    UIGraphicsBeginImageContext(cell.contentView.bounds.size);
    [cell.contentView.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
 
    UIImageView *iv = [[UIImageView alloc] initWithImage:img];
    self.dragAndDropView = [[UIView alloc]initWithFrame:iv.frame];
    [self.dragAndDropView addSubview:iv];
    [self.dragAndDropView setBackgroundColor:[UIColor blueColor]];
    [self.dragAndDropView setCenter:[gesture locationInView:self.view.superview]];
 
    [self.view.superview addSubview:self.dragAndDropView];
}

In the draggingGestureDidBegin I remove the cell from the table to make it look like the cell has been popped out of the table. You have to have a handle to the item that this cell represents so you can add it to the correct view when the user drops the view. The final method here returns the dragged view created in the draggingGestureWillBegin so that this can be used in the DragAndDropTableViewController to attach the gesture that was originally associated with the cell selected.

-(void)dragAndDropTableViewController:(DragAndDropTableViewController *)ddtvc draggingGestureDidBegin:(UIGestureRecognizer *)gesture forCell:(UITableViewCell *)cell;
{    
    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
    self.selectedChoice =[self.choices objectAtIndex:indexPath.row];     
    [self.choices removeObjectAtIndex:[self.choices indexOfObject:self.selectedChoice]];
    [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationFade];
}
 
-(UIView *)dragAndDropTableViewControllerView:(DragAndDropTableViewController *)ddtvc{
    return self.dragAndDropView;
}

The final two delegate methods are very straight forward, in draggingGestureDidMove you must remember to update the position of the draggable view when the gesture moves and in draggingGestureDidEnd remove the dragged view when dragging ends.

-(void)dragAndDropTableViewController:(DragAndDropTableViewController *)ddtvc draggingGestureDidMove:(UIGestureRecognizer *)gesture{
    [self.dragAndDropView setCenter:[gesture locationInView:self.view.superview]];
}
 
-(void)dragAndDropTableViewController:(DragAndDropTableViewController *)ddtvc draggingGestureDidEnd:(UIGestureRecognizer *)gesture{    
    [self.dragAndDropView removeFromSuperview];
    self.dragAndDropView = nil;
}

The final piece of the jigsaw is to handle adding the item when the user drops on another view. This is done in the droppedGesture

-(void)dragAndDropTableViewController:(DragAndDropTableViewController *)ddtvc droppedGesture:(UIGestureRecognizer *)gesture{
 
    UIView *viewHit = [self.view hitTest:[gesture locationInView:self.view.superview] withEvent:nil];
 
    if([ddtvc isKindOfClass:[ChoicesTableViewController class]]){
        ChoicesTableViewController *fromTBCV=(ChoicesTableViewController *)ddtvc;
        id selectedChoice = fromTBCV.selectedChoice;
 
        if([fromTBCV.view isEqual:viewHit])
            [fromTBCV.choices addObject:selectedChoice];
        else if([viewHit.superview isKindOfClass:[UITableViewCell class]] && [fromTBCV.view isEqual:viewHit.superview.superview]){
            //we have dropped on a cell in our table
            NSIndexPath *ip = [fromTBCV.tableView indexPathForCell:(UITableViewCell *)viewHit.superview];
            [fromTBCV.choices insertObject:selectedChoice atIndex:ip.row];
        }
        else if([self.selectedChoicesViewController.view isEqual:viewHit]){
            [self.selectedChoicesViewController.selectedChoices addObject:selectedChoice];
            [self.selectedChoicesViewController.tableView reloadData];
        }
        else if([viewHit.superview isKindOfClass:[UITableViewCell class]] && [self.selectedChoicesViewController.view isEqual:viewHit.superview.superview]){
            //we have dropped on a cell in our table
            NSIndexPath *ip = [self.selectedChoicesViewController.tableView indexPathForCell:(UITableViewCell *)viewHit.superview];
            [self.selectedChoicesViewController.selectedChoices insertObject:selectedChoice atIndex:ip.row];
            [self.selectedChoicesViewController.tableView reloadData];
        }   
    }
    else if([ddtvc isKindOfClass:[SelectedChoicesTableViewController class]]){
        SelectedChoicesTableViewController *fromSCTVC=(SelectedChoicesTableViewController *)ddtvc;
        id selectedChoice = fromSCTVC.selectedChoice;
 
        if([fromSCTVC.view isEqual:viewHit])
            [fromSCTVC.selectedChoices addObject:selectedChoice];
        else if([viewHit.superview isKindOfClass:[UITableViewCell class]] && [fromSCTVC.view isEqual:viewHit.superview.superview]){
            //we have dropped on a cell in our table
            NSIndexPath *ip = [fromSCTVC.tableView indexPathForCell:(UITableViewCell *)viewHit.superview];
            [fromSCTVC.selectedChoices insertObject:selectedChoice atIndex:ip.row];
        }
        else if([self.choicesViewController.view isEqual:viewHit]){
            [self.choicesViewController.choices addObject:selectedChoice];
            [self.choicesViewController.tableView reloadData];
        }
        else if([viewHit.superview isKindOfClass:[UITableViewCell class]] && [self.choicesViewController.view isEqual:viewHit.superview.superview]){
            //we have dropped on a cell in our table
            NSIndexPath *ip = [self.choicesViewController.tableView indexPathForCell:(UITableViewCell *)viewHit.superview];
            [self.choicesViewController.choices insertObject:selectedChoice atIndex:ip.row];
            [self.choicesViewController.tableView reloadData];
        }        
    }
    else
        NSLog(@"Item dropped on a non droppable area");
}

At the moment if you drop the cell on a view that I haven’t handled for adding items through this method the item is effectively deleted but you could handle this to make the view bounce back the view it came from or use the draggingGestureDidMove to detect the view the item is over to show that it will be deleted similar to the smoke puff for items being pulled from the dock on Mac OS X. I would love to here your feedback to improve this as I think this could be something that is very useful for lots of people here is the code for the DragAndDropTutorial for you to give it a go.