Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 543
Cosmin Avatar asked Feb 28 '12 12:02

Cosmin


2 Answers

Manipulating rows and sections into UITableView using animation is made pretty easy using the following API's (emphasis on the last two):

  • insertRowsAtIndexPaths:withRowAnimation:
  • insertSections:withRowAnimation:
  • deleteRowsAtIndexPaths:withRowAnimation:
  • deleteSections:withRowAnimation:
  • beginUpdates
  • endUpdates

Updating the Data Model

You should update your data model anywhere between the beginUpdates and endUpdates methods. It doesn't matter if you update your data model before or after insertion or deletion methods just so long as you do it between the beginUpdates and endUpdates methods.

Do not Insert Rows for New Sections You are Inserting

When adding a new section using the insertSections:withRowAnimation: method, you do not need to call insertRowsAtIndexPaths:withRowAnimation: to add rows into it. The insertRowsAtIndexPaths:withRowAnimation: method is just for animating an existing section changing. Similarly, you do not need to call deleteRowsAtIndexPaths:withRowAnimation: when you remove a section using deleteSections:withRowAnimation:.

Regarding Deleting and Inserting:

I generally do these in separate beginUpdates: endUpdates calls, however you can insert and delete at the same time. Just be aware that the UITableView will first try to delete rows / sections and then try to insert them regardless of the order you do it in your code.

Discussion part of deleteRowsAtIndexPaths:withRowAnimation:

Note the behavior of this method when it is called in an animation block defined by the beginUpdates and endUpdates methods. UITableView defers any insertions of rows or sections until after it has handled the deletions of rows or sections. This happens regardless of ordering of the insertion and deletion method calls. This is unlike inserting or removing an item in a mutable array, where the operation can affect the array index used for the successive insertion or removal operation. For more on this subject, see “Batch Insertion and Deletion of Rows and Sections” in Table View Programming Guide for iOS.

Example of inserting into a section:

int indexOfNewSection = 4; // TODO: change to meaningful value
[self.tableview beginUpdates];
[self.tableview insertSections:[NSIndexSet indexSetWithIndex:indexOfNewSection]
              withRowAnimation:UITableViewRowAnimationAutomatic];

// Update data model
[self.sections insertObject:sectionObj atIndex:indexOfNewSection];

[self.tableview endUpdates];
like image 169
Sam Avatar answered Nov 16 '22 00:11

Sam


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.

like image 43
QED Avatar answered Nov 15 '22 23:11

QED