Ipphones

  • Subscribe to our RSS feed.
  • Twitter
  • StumbleUpon
  • Reddit
  • Facebook
  • Digg

Saturday, 25 October 2008

Table View Multi-Row Edit Mode

Posted on 12:27 by Unknown

If you've played around at all with UITableView's "Edit Mode", you've probably been disappointed that it doesn't support the ability to select and then delete multiple rows in the table, the way that you can do in Apple's Mail application. It was one of the most welcome improvements made in the 2.0 iPhone OS, and I was a bit bummed when I discovered that the ability to do that was not being added to UITableView with the 2.0 release.

Personally, I'd like to see the functionality in more table-based iPhone apps, so I threw together a little sample iPhone project that shows how to do it. You can find the project Right Here.

I'm not going to walk through the entire projects, as most of it is standard application-building, but I wanted to point out the general approach I used. I do not know if this is exactly how Mail does it, and I'm certainly not sure this is the best way to do it, but it does work. The only aspect of the Mail implementation I didn't get is changing the background color of the selected rows in edit mode. I tried it, but was getting some weird behavior where the last-selected row was turning back to white, but when another row was added to the selection, then it would return to the correct color. At some point, I'll dive in and try and figure out what was wrong with that code, but for the time being, this should work pretty well for you and at least give you an idea of how the process can work.

In my controller class, I defined a few constants and macros:

#define kCellImageViewTag 1000
#define kCellLabelTag 1001

#define kLabelIndentedRect CGRectMake(40.0, 12.0, 275.0, 20.0)
#define kLabelRect CGRectMake(15.0, 12.0, 275.0, 20.0)

The first two will be used later to retrieve the correct subviews of the table view cell. The bottom two define the possible positions of the row's text. If we're in edit mode, the text is going to be moved over a little bit (which will be animated). By defining the two rects here, we can shift the text over easily by simply assigning the new value to the label's frame property.

In my controller, I have two mutable arrays. One will hold the display values, the other will be used to hold which rows are selected when we're in edit mode. I also define a BOOL that will identify when we're in edit mode. I don't call it edit mode because I don't want to risk a name conflict or confusion with Apple's code.

NSMutableArray *countries;
NSMutableArray *selectedArray;
BOOL inPseudoEditMode;

I also have two UIImage pointers that contain the checked and unchecked image. This is a little klugey - the unchecked is just a circle, so I probably should have just used CoreGraphics to draw the circle, but this was easier.

UIImage *selectedImage;
UIImage *unselectedImage;

I created a method that will create the selectedArray, populating it with NSNumber objects that hold a NO value for every row. This lets me easily reset the selection after a delete.

- (void)populateSelectedArray
{
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:[countries count]];
for (int i=0; i < [countries count]; i++)
[array addObject:[NSNumber numberWithBool:NO]];
self.selectedArray = array;
[array release];
}

This method gets called in viewDidLoad, and also every time we delete rows. The viewDidLoad method also loads an array with strings pulled from a text file and loads the two UIImages.

There's an IBAction method to toggle edit mode. This gets called when the user presses the "Delete" button in the Nav Bar. It changes the value of inPsuedoEditMode and also hides or unhides the toolbar at the bottom, which has the "Delete" button that causes selected rows to get deleted.

-(IBAction)togglePseudoEditMode
{
self.inPseudoEditMode = !inPseudoEditMode;
toolbar.hidden = !inPseudoEditMode;

[self.tableView reloadData];
}


Most of the work here is in the tableView:cellForRowAtIndexPath: method. Here is where we have to look at whether we're in edit mode, and if we are in edit mode, look at which rows are selected.


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


UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:EditCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:EditCellIdentifier] autorelease];


UILabel *label = [[UILabel alloc] initWithFrame:kLabelRect];
label.tag = kCellLabelTag;
[cell.contentView addSubview:label];
[label release];

UIImageView *imageView = [[UIImageView alloc] initWithImage:unselectedImage];
imageView.frame = CGRectMake(5.0, 10.0, 23.0, 23.0);
[cell.contentView addSubview:imageView];
imageView.hidden = !inPseudoEditMode;
imageView.tag = kCellImageViewTag;
[imageView release];

}

[UIView beginAnimations:@"cell shift" context:nil];

UILabel *label = (UILabel *)[cell.contentView viewWithTag:kCellLabelTag];
label.text = [countries objectAtIndex:[indexPath row]];
label.frame = (inPseudoEditMode) ? kLabelIndentedRect : kLabelRect;

UIImageView *imageView = (UIImageView *)[cell.contentView viewWithTag:kCellImageViewTag];
NSNumber *selected = [selectedArray objectAtIndex:[indexPath row]];
imageView.image = ([selected boolValue]) ? selectedImage : unselectedImage;
imageView.hidden = !inPseudoEditMode;
[UIView commitAnimations];

return cell;
}

Notice a few things

  • we manually create subviews to the table view cell's content view, and we assign them tags. The tags allow us to retrieve the correct subview when we get dequeued cell instead of creating a new one.

  • We call beginAnimations:forContext: and commitAnimations: around our changes so that the changes get animated for us. That's all we have to do to make turning edit mode on and off animated


When a row is touched, and we are in edit mode, we have to set the corresponding row in the selection array to YES if it's currently NO and vice versa.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
if (inPseudoEditMode)
{
BOOL selected = [[selectedArray objectAtIndex:[indexPath row]] boolValue];
[selectedArray replaceObjectAtIndex:[indexPath row] withObject:[NSNumber numberWithBool:!selected]];
[self.tableView reloadData];
}
}

There's one more method that's key to this process, which is another action method that gets called when the Delete button in the toolbar gets pressed. Because this toolbar is only shown when we're in edit mode, we don't have to check that, we just do the delete. Because you can't delete objects from a collection while enumerating over them, this method is a little more complex than you might expect.

-(IBAction)doDelete
{
NSMutableArray *rowsToBeDeleted = [[NSMutableArray alloc] init];
NSMutableArray *indexPaths = [[NSMutableArray alloc] init];
int index = 0;
for (NSNumber *rowSelected in selectedArray)
{
if ([rowSelected boolValue])
{

[rowsToBeDeleted addObject:[countries objectAtIndex:index]];
NSUInteger pathSource[2] = {0, index};
NSIndexPath *path = [NSIndexPath indexPathWithIndexes:pathSource length:2];
[indexPaths addObject:path];
}
index++;
}

for (id value in rowsToBeDeleted)
{
[countries removeObject:value];
}

[self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];

[indexPaths release];
[rowsToBeDeleted release];
inPseudoEditMode = NO;
[self populateSelectedArray];
[self.tableView reloadData];
}

Anyway, I hope this is helpful to some people. If you have any questions, put them in the comments, or drop me an e-mail.
Email ThisBlogThis!Share to XShare to Facebook
Posted in Cocoa Touch, Edit Mode, iPhone SDK, Table Views | No comments
Newer Post Older Post Home

0 comments:

Post a Comment

Subscribe to: Post Comments (Atom)

Popular Posts

  • Making OpenGL ES Screenshot
    The Bit-101 Blog has an entry that shows how to take a screenshot when using OpenGL ES . I tested this in my much-delayed particle-generato...
  • Adding CLANG to Your Build Process
    Frasier Spiers has a nifty piece this morning on using Git pre-commit hooks to automatically run the CLANG Static Analyzer. I'm not a G...
  • CLANG Static Analyzer
    If you aren't using the LLVM/Clang Static Analyzer , you really should be. The Clang Project is an attempt to write a front end for the...
  • A Little Help
    I'm having a problem with OpenGL ES, and it's keeping me from finishing my particle engine post. I was hoping someone here could see...
  • WWDC Accommodations
    Staying downtown in San Francisco is very expensive in the summertime. Bu, if you're going to WWDC, you really want to stay downtown. Yo...
  • Xcode File Templates and a Mystery
    One of the things that confuses many newcomers to Xcode is how to set it up so that your company name gets automatically filled in when you ...
  • Brain Surgery?
    Craig Hockenberry has an interesting post on his blog today about the iPhone background processing issue. Craig speaks from personal experi...
  • Book's Almost Done
    I just finished Chapter 16. I'll give it another read-over in the morning then it will go off to my writing partner for his review, then...
  • iPhone Alley
    Looks like Dave and I are going to make an appearance on the iPhone Alley Podcast next week. We're recording on Sunday night, so I woul...
  • Shuffling Arrays
    Ever want to randomize an array of items? It's a task that, for some reason, I've had to do a lot in recent programs. So, I wrote a ...

Categories

  • 3D Models
  • Ad Hoc Distribution
  • ADC
  • Address Book
  • Amazon
  • Anaglyphs
  • App Store
  • Apple
  • Apple DTS
  • Apple Store
  • Application Store
  • articles
  • Award
  • Background Processing
  • Barcodes
  • Beta
  • Blog
  • Blogger
  • Blogging
  • Blogs
  • Blogspot
  • Book project
  • Bug Reporting
  • Captain Obvious
  • Categories
  • Censorship
  • CFFoundation
  • CGAffineTransform
  • Clang Static Analyzer
  • Cocoa
  • Cocoa Touch
  • Code Reuse
  • Code Signing
  • Computer
  • conferences
  • Controller Classes
  • Core Animation
  • Daring Fireball
  • Database
  • Debugging
  • Defect
  • Delegates
  • Design Awards
  • Developer Certifications
  • Discussion Forums
  • Edit Mode
  • employment opportunities
  • Encryption
  • Enterprise
  • Errata
  • free code
  • Free software
  • Full Screen
  • Game Programming
  • Gestures
  • Getting Started
  • goof
  • Google Code
  • Google Maps
  • Gotcha
  • Help
  • HIG
  • HTTP PUT
  • Idiots
  • Idle Timer
  • Images
  • Instruments
  • Interface Builder
  • iPHone
  • iPhone Applications
  • iPhone Dev Center
  • iPhone Developers
  • iPhone OS 3.0
  • iPhone SDK
  • iPhone SDK PNG
  • iPhone Simulator
  • iPhoneSDK
  • iPod
  • Job Opportunities.
  • k
  • Key Value Observing
  • Keynote
  • KVO
  • Landscape Mode
  • Learn Cocoa
  • Learn Cocoa on the Mac
  • libxml
  • Licensing
  • Mac Developers
  • Mac OS X
  • Macworld Expo
  • Microsoft
  • NDA
  • NeHe
  • New Category
  • New Release
  • NSFileHandle
  • NSMutableArray
  • NSMutableURLRequest
  • NSXML
  • Object-Oriented Design
  • Objective-C
  • Open Source
  • OpenGL ES
  • Optimizations
  • Other blogs
  • Paired Arrays
  • Parsing
  • Particle Engine
  • Party
  • PeopleSoft
  • Performance
  • Persistence
  • Pink Screen of Death
  • Piracy
  • Pixar
  • Podcasts
  • Press Release WTF
  • Press Releases WTF
  • private APIs Google
  • Project Template
  • Properties
  • Random Numbers
  • Rant
  • Rejected
  • Resources
  • Responder Chain
  • REST
  • Reverse Engineering
  • Rumors
  • Runtime
  • Sample Code
  • Screencast
  • screenshot
  • Scroll Views
  • snippet
  • Snow Leopard.
  • SOAP
  • Sockets
  • Source
  • Splash Screen
  • SQLite
  • SQLitePersistentObjects
  • Steve Jobs
  • Steve-Note
  • Strings
  • Stupidity
  • Subversion
  • Table Views
  • Taps
  • Template
  • Tip
  • Tips
  • Tririga
  • tutorials
  • Twitter
  • UIAlertView
  • UIColor
  • UIImage
  • UIPickerView
  • UIScrollView
  • UITextField
  • UIView
  • UIWebView
  • Update
  • Utilities
  • UUID
  • Vacation
  • Version Control
  • Web Services
  • Writing
  • WTF
  • WWDC
  • Xcode
  • XML

Blog Archive

  • ►  2009 (141)
    • ►  May (14)
    • ►  April (30)
    • ►  March (48)
    • ►  February (26)
    • ►  January (23)
  • ▼  2008 (163)
    • ►  December (46)
    • ►  November (25)
    • ▼  October (44)
      • Elvis has Left the Building
      • Updated SQLitePersistence
      • Some More Open Source
      • Speaking of Wil Shipley
      • 500%
      • Better Template
      • CGAffineTransform 1.1 - A little more
      • Demystifying CGAffineTransform
      • I'm Flattered, I Think...
      • WSDL2ObjC
      • Table View Multi-Row Edit Mode
      • Captain Obvious Strikes Again
      • It's Beta, but it Shows they are listening...
      • SOAP Web Services Redux
      • Crimson FX Open Sourced
      • Another Open Source Project
      • Scroll Views
      • Cocoa Barcodes
      • Old is New
      • You Shouldn't, but You Will...
      • Getting the contents of a UIView as a UIImage
      • Shuffling Arrays
      • Encodings Can Byte You
      • Idle Timer
      • Starting in Landscape Mode without Status Bar
      • SQLite Persistent Object Update
      • Device vs. Simulator
      • Another Couple of Tips by Captain Obvious
      • Simulator
      • iPhone "Optimized" PNGs
      • Random Thoughts: rand() vs. arc4random()
      • Twitter
      • Another ADC Article Online
      • Completely Off-Topic
      • A Couple More Math Snippets for 2D Graphics
      • Circles, Ellipses, and Regular Polygons in OpenGL ES
      • Radar URLs & Bug Reporting
      • A Little Color in your Life
      • I Really Like the Layout
      • Did you know you could take screenshots without Xc...
      • A Bit About the Responder Chain
      • Handling Double Taps
      • Ego Boost
      • Holy %@#$!
    • ►  September (2)
    • ►  August (5)
    • ►  July (2)
    • ►  June (9)
    • ►  May (2)
    • ►  April (11)
    • ►  March (17)
Powered by Blogger.

About Me

Unknown
View my complete profile