Letting users delete rows isn’t really significantly harder than letting them move rows. Let’s take a look at that process. Instead of creating an array from a hard-coded list of objects, we’re going to load a property list file this time, just to save some typing. You can grab the file called computers.plist out of the 09 Nav folder in the projects archive that accompanies this book and add it to the Resources folder of your Xcode project.
Select the Classes folder in the Groups & Files pane in Xcode, and then press ⌘N or select New File… from the File menu. Select Cocoa Touch Class, select Objective-C class and NSObject for Subclass of. When prompted for a name, this time type DeleteMeController.m.
Once you’ve got your new files, let’s start by editing DeleteMeController.h. The changes we’re going to make there should look familiar, as they’re nearly identical to the ones we made in the last view controller we built. Go ahead and make these changes now:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "SecondLevelViewController.h"
@interface DeleteMeController : NSObject {
@interface DeleteMeController : SecondLevelViewController { NSMutableArray *list;
}
@property (nonatomic, retain) NSMutableArray *list;
-(IBAction)toggleEdit:(id)sender;
@end
No surprises here, right? We’re changing the superclass from NSObject to Second LevelViewController. After that, we declare a mutable array to hold our data and an action method to toggle edit mode. In the last controller we built, we used edit mode to let the users reorder rows. In this version, edit mode will be used to let them delete rows.
You can actually combine both in the same table if you like. We separated them so the con- cepts would be a bit easier to follow, but the delete and reorder operations do play nicely together. A row that can be reordered will display the reorder icon anytime that the table is in edit mode. When you tap the red circular icon on the left side of the row (see Figure 9-7), the Delete button will pop up, obscuring the reorder icon but only temporarily.
Download at Boykma.Com
Switch over to DeleteMeController.m, and add the following code:
#import "DeleteMeController.h"
@implementation DeleteMeController
@synthesize list;
-(IBAction)toggleEdit:(id)sender {
[self.tableView setEditing:!self.tableView.editing animated:YES];
if (self.tableView.editing)
[self.navigationItem.rightBarButtonItem setTitle:@"Done"];
else
[self.navigationItem.rightBarButtonItem setTitle:@"Delete"];
}
- (void)viewDidLoad { if (list == nil) {
NSString *path = [[NSBundle mainBundle]
pathForResource:@"computers" ofType:@"plist"];
NSMutableArray *array = [[NSMutableArray alloc]
initWithContentsOfFile:path];
self.list = array;
[array release];
}
UIBarButtonItem *editButton = [[UIBarButtonItem alloc]
initWithTitle:@"Delete"
style:UIBarButtonItemStyleBordered target:self
action:@selector(toggleEdit:)];
self.navigationItem.rightBarButtonItem = editButton;
[editButton release];
[super viewDidLoad];
}
- (void)dealloc { [list release];
[super dealloc];
}
#pragma mark -
#pragma mark Table Data Source Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [list count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *DeleteMeCellIdentifier = @"DeleteMeCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
DeleteMeCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:DeleteMeCellIdentifier] autorelease];
}
NSInteger row = [indexPath row];
cell.textLabel.text = [self.list objectAtIndex:row];
return cell;
}
#pragma mark -
#pragma mark Table View Data Source Methods - (void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger row = [indexPath row];
[self.list removeObjectAtIndex:row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
@end
Let’s look at what we did. The new action method, toggleEdit:, is pretty much the same as our last version. It sets edit mode to on if it’s currently off and vice versa, and then sets the button’s title as appropriate. The viewDidLoad method is also similar to the one from the previous view controller and, again, we have no viewDidUnload method because we have no outlets and we want to preserve changes made to our mutable array in edit mode.
The only difference is that we’re loading our array from a property list rather than feeding it a hard-coded list of strings. The property list we’re using is a flat array of strings containing a variety of computer model names that might be a bit familiar. We also assign a different name to the edit button this time, naming it Delete to make the button’s effect obvious to the user.
The two data source methods contain nothing new, but the last method in the class is some- thing you’ve never seen before, so let’s take a closer look at it:
- (void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
Download at Boykma.Com
This method is called by the table view when the user has made an edit, which means a delete or an insert. The first argument is the table view on which a row was edited. The second parameter, editingStyle, is a constant that tells us what kind of edit just happened. Currently, there are three editing styles defined. One of them is UITable ViewCellEditingStyleNone, which we used in the last section to indicate that a row can’t be edited. The other two styles are UITableViewCellEditingStyleDelete, which is the default option, and UITableViewCellEditingStyleInsert. The option UITableView CellEditingStyleNone will never be passed into this method, because it is used to indicate that editing is not allowed for this row.
We ignore this parameter, because the default editing style for rows is the delete style, so we know that every time this method is called, it will be requesting a delete. You can use this parameter to allow both inserts and deletes within a single table. The other editing style,
UITableViewCellEditingStyleInsert, is generally used when you need to let the user insert rows at a specific spot in a list. In a list whose order is maintained by the system, such as an alphabetical list of names, the user will usually tap a toolbar or navigation bar button to ask the system to create a new object in a detail view. Once the user is done specifying the new object, the system will place in the appropriate row. We won’t be covering the use of inserts, but the insert functionality works in fundamentally the same way as the delete we are about to implement. The only difference is that, instead of deleting the specified row from your data model, you have to create a new object and insert it at the specified spot.
The last parameter, indexPath, tells us which row is being edited. For a delete, this index path represents the row to be deleted. For an insert, it represents the index where the new row should be inserted.
In our method, we first retrieve the row that is being edited from indexPath:
NSUInteger row = [indexPath row];
Then, we remove the object from the mutable array we created earlier:
[self.list removeObjectAtIndex:row];
Finally, we tell the table to delete the row, specifying the constant UITableViewRow AnimationFade, which represents one type of animation the iPhone will use when remov- ing rows. There are several other options in addition to this one, which causes the row to fade away. You can look up the UITableViewRowAnimation in Xcode’s document browser to see what other animations are available.
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
And that’s all she wrote, folks. That’s the whole enchilada for this class, so let’s add an instance of it to our root view controller and try it out. In FirstLevelViewController.m, we first need to import our new controller class’s header file, so add the following line of code right before the @implementation declaration:
#import "DeleteMeController.h"
Now, add the following code to the viewDidLoad method:
- (void)viewDidLoad {
self.title = @"First Level";
NSMutableArray *array = [[NSMutableArray alloc] init];
// Disclosure Button
DisclosureButtonController *disclosureButtonController = [[DisclosureButtonController alloc]
initWithStyle:UITableViewStylePlain];
disclosureButtonController.title = @"Disclosure Buttons";
disclosureButtonController.rowImage = [UIImage imageNamed:
@"disclosureButtonControllerIcon.png"];
[array addObject:disclosureButtonController];
[disclosureButtonController release];
// Check List
CheckListController *checkListController = [[CheckListController alloc]
initWithStyle:UITableViewStylePlain];
checkListController.title = @"Check One";
checkListController.rowImage = [UIImage imageNamed:
@"checkmarkControllerIcon.png"];
[array addObject:checkListController];
[checkListController release];
// Table Row Controls
RowControlsController *rowControlsController = [[RowControlsController alloc]
initWithStyle:UITableViewStylePlain];
rowControlsController.title = @"Row Controls";
rowControlsController.rowImage = [UIImage imageNamed:
@"rowControlsIcon.png"];
[array addObject:rowControlsController];
[rowControlsController release];
// Move Me
MoveMeController *moveMeController = [[MoveMeController alloc]
initWithStyle:UITableViewStylePlain];
moveMeController.title = @"Move Me";
moveMeController.rowImage = [UIImage imageNamed:@"moveMeIcon.png"];
Download at Boykma.Com
[array addObject:moveMeController];
[moveMeController release];
// Delete Me
DeleteMeController *deleteMeController = [[DeleteMeController alloc]
initWithStyle:UITableViewStylePlain];
deleteMeController.title = @"Delete Me";
deleteMeController.rowImage = [UIImage imageNamed:@"deleteMeIcon.png"];
[array addObject:deleteMeController];
[deleteMeController release];
self.controllers = array;
[array release];
[super viewDidLoad];
}
Save everything, compile, and let her rip. When the simulator comes up, the root level will now have—can you guess?—five rows. If you select the new Delete Me row, you’ll be presented with a list of computer models (see Figure 9-21). How many of these have you owned?
Notice that we again have a button on the right side of the navigation bar, this time labeled Delete. If we tap that, the table enters edit mode, which looks like Figure 9-22.
Figure 9-21. The delete me view when it first launches
Figure 9-22. The delete me view in edit mode
Next to each editable row is now a little icon that looks a little like a “Do Not Enter”
street sign. If you tap the icon, it rotates sideways, and a button labeled Delete appears (see Figure 9-7). Tapping that button will cause its row to be deleted, both from the underly- ing model as well as from the table, using the animation style we specified.
And when you implement edit mode to allow deletes, you get additional functionality for free. Swipe your finger horizontally across a row. Look at that! The delete button comes up for just that row, just like in the Mail application.