Cosmin Cosmin - 2 months ago 22
iOS Question

Animate the insertion of a new section in UITableVIew

I have this button on my view, and when I press it I insert a new section in my table view ( I have a logical condition in my

-(NSInteger) numberOfSectionsInTableView:(UITableView*)tableView
{
if (slide==TRUE) return 2;
return 1;
}


And also in my
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
. My section is added as it should, but I have read somewhere that this can be animated, because when I press my button the section is added but with no animation. I think I should use this
-(void)insertSections:(NSIndexSet*)sections withRowanimation(UITableViewRowAnimation) animation
but I haven't found a proper example on the web.

QED QED
Answer

UITableViewRowAnimation is an enum declared at the top of UITableView.h You can also view it in the UITableView reference. It's smallish, so I'll just paste it!

typedef enum {
    UITableViewRowAnimationFade,
    UITableViewRowAnimationRight,           // slide in from right (or out to right)
    UITableViewRowAnimationLeft,
    UITableViewRowAnimationTop,
    UITableViewRowAnimationBottom,
    UITableViewRowAnimationNone,            // available in iOS 3.0
    UITableViewRowAnimationMiddle,          // available in iOS 3.2.  attempts to keep cell centered in the space it will/did occupy
    UITableViewRowAnimationAutomatic = 100  // available in iOS 5.0.  chooses an appropriate animation style for you
} UITableViewRowAnimation;

Basically it tells the table view from which direction you want the rows/sections to be animated in/out. A little experimenting will demonstrate the effect of each.

For example, inserting a row with UITableViewRowAnimationTop will trigger an animation which gives the impression of a row coming into the table view from a space immediately above that of its final destination in the table view.

So your insertion might look like:

-(void)sliderValueChanged:(id)slider {
   slide = slider.on;

   [tableView beginUpdates];

   if (slider.on) {
      [tableView insertSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationTop];
      // TODO: update data model by inserting new section
   } else {
      [tableView deleteSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationTop];
      // TODO: update data model by removing approprite section
   }

   [tableView endUpdates];
}

And you will have to be sure that your delegate and data source provide info that is consistent with your assertion to insert sections/rows. From your question, it looks like you have done so.

EDITS:

I don't think you have to call reloadData. UITableView requires that your data model reflect the changes you make with with the insert/delete methods. So that, for example, if, before a call to insertSections:withRowAnimation: (with which you are inserting a single section), your numberOfSectionsInTableView: method returned 1, then, after the call, it must return 2. To do otherwise throws an exception. It is this enforcement of consistency that allows you (again, I think) to avoid the call to reloadData - the necessary data is being reloaded by the whole beginUpdates: endUpdates: transaction, and any updates to the model you make during that transaction must match one-for-one your insert/delete calls.

Clear as mud?

UPDATES

If you were programming explicit animations, then I would say that you could do it in the 'completion handler' (or just program the animation directly for that matter), but that is not available to you here. I think you can wrap the button-presenting code in its own method in the view, then set a timer to call it after a short amount of time, say, .2 seconds (you'll have to experiment to see what looks good). e.g.

[NSTimer scheduledTimerWithTimeInterval:.2 target:self selector:@selector(presentButton) userInfo:nil repeats:NO];

That should do the trick.