views:

462

answers:

1

I have an UITableView with sections in my iPhone application where it looks as something similar to viewing a contact details in standard contacts app. In one of the sections I have a UISegmentedControl set as a header of the section and the items in that section depend on what segment is selected in the segmented control. What I wanted to do is to have animations shifting the rows of one tab out of the view to one side and shifting the rows of another tab in from the other side. I have managed to achieve this by inserting/deleting the respective rows with default UITableView's animations for that but it looks a bit weird when the number of rows in one tab is different from the other one. Also the code for that is quite nasty.

Here's the code I have (simplified a bit):

[self.tableView beginUpdates];
UITableViewRowAnimation delAnim = UITableViewRowAnimationRight;
UITableViewRowAnimation insAnim = UITableViewRowAnimationLeft;
NSUInteger numdel = [self.list count];
NSUInteger numins = [newlist count];
NSMutableArray* indices = [NSMutableArray arrayWithCapacity: numdel];
for (NSUInteger i = 0; i < numins; i++)
{
 [indices addObject: [NSIndexPath indexPathForRow: i  inSection: SECT]];
}
[self.tableView insertRowsAtIndexPaths: indices  withRowAnimation: insAnim];
[indices removeAllObjects];
for (NSUInteger i = 0; i < numdel; i++)
{
 [indices addObject: [NSIndexPath indexPathForRow: i  inSection: SECT]];
}
[self.tableView deleteRowsAtIndexPaths: indices  withRowAnimation: delAnim];
self.list = newlist;
[self.tableView endUpdates];

I'm fairly new to Objective C and Cocoa, so any advices are very welcome.

+3  A: 

You could try using a separate table view which you animate in directly. The snippet here is just typed off-the-cuff, so might need some work, but it should give you some pointers at least:

- (void) switchToNewTableFromRight
{
    UITableView * newTableView = [[UITableView alloc] initWithFrame: self.tableView.frame style: self.tableView.style];

    // put it off to the right of the existing table
    CGRect frame = newTableView.frame;
    frame.origin.x += frame.size.width;
    newTableView.frame = frame;

    // set data for new table
    // you should ensure you're setup to supply data for the new table here, btw
    newTableView.delegate = self;
    newTableView.dataSource = self;
    [newTableView reloadData];

    // add to parent of current table view at this (offscreen) location
    [self.tableView.superview addSubview: newTableView];

    // now we animate
    [UIView beginAnimations: @"TableFromRight" context: newTableView];

    // set the function it should call when the animation completes
    [UIView setAnimationDelegate: self];
    [UIView setAnimationDidStopSelector: @selector(animation:finished:context:)];

    // set new table's frame to current table's frame
    newTableView.frame = self.tableView.frame;

    // set current table's frame to be offscreen to the left
    frame = self.tableView.frame;
    frame.origin.x -= frame.size.width;
    self.tableView.frame = frame;

    // commit the animations to start them going
    [UIView commitAnimations];
}

- (void) animation: (NSString *) animationID finished: (BOOL) finished context: (void *) context
{
    // could be a good idea to check that finished == YES here
    UITableView * newTableView = (UITableView *) context;
    self.tableView = newTableView;

    // newTableView has been inited but not autoreleased, etc.
    // now the controller (self) owns it, so release that first reference
    [newTableView release];
}

The idea here is that you setup the new table (making it the same size as the existing one), place it offscreen to the right of the existing table, and then animate the movement of both tables left by their width. This way the existing table will shift offscreen while the new one will shift onscreen. When the animation completes it will call the supplied method, giving you the opportunity to make the new table view the official table view.

Another option would be to use a flip transition, which might go something like this:

// setup new table
UITableView * newTableView = [[UITableView alloc] initWithFrame: self.tableView.frame style: self.tableView.style];
newTableView.delegate = self;
newTableView.dataSource = self;
[newTableView reloadData];

[UIView beginAnimations: nil context: NULL];
[UIView setAnimationDuration: 1.0];
[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromRight forView: self.tableView.superview cache: YES];

// generally here you'd remove the old view and add the new view
// I'm *assuming* that UITableViewController's -setTableView: will do the same thing
self.tableView = newTableView;

[UIView commitAnimations];

Hopefully one of those will have the desired effect.

Jim Dovey
Thanks for your insights! The problem though is that I only want to animate a single section of a table not the whole table and your solution seems to be animating the whole thing from what I can see. Maybe I should make a separate thing for the tabbing area and work with it but the problem is that I do like the look of the standard UITableView and I do like that I can scroll the content above tabs out of view so I'm not sure which way to go.
inkredibl
Ah, didn't notice that (doh, non-observant person alert!). If it's a single section you want to change, you can use NSTableView's -deleteSection:atIndex: and insertSection:atIndex: to do what you're looking for, I think. You may want to avoid -beginUpdates/-endUpdates though, it's been a bit flaky for me. I wound up doing my deletions then setting up a 0.3-second timer to do the insertions. A bit more prone to breaking if the implicit animation duration ever changes, but might be useful to try if you're still getting graphical anomalies.
Jim Dovey