How to delete row in UITableView and update indexPaths without calling reloadData?

[UPDATE]

I've found the problem. I've created a custom UITableViewCell, and am not using the standard methods to trigger deletion. My custom UITableViewCell has a custom indexPath property that I populate when creating the cell. That's why it doesn't get updated without a call to reload data.

As a workaround, to minimize code rewriting, I'm using the solution marked as the answer.

[/UPDATE]

I've seen other questions similar to this, but not quite the same...

I have a UITableView that gets it's rows (cells) from objects in a NSArray. The rows are mapped to the NSArray using the indexPath.row. This means that the object at index 0 in the NSArray corresponds to the cell with indexPath.row 0.

Whenever I delete a row, I remove the object from the NSArray (as seen in many examples, including Apple's documentation).

The problem is, whenever a row is removed, the indexPath's aren't updated. For example:

I have a table view with 3 rows. This means I have indexPath.row 0, 1 and 2. Also, the corresponding NSArray objects a index 0, 1 and 2.

When I delete row 1 from the UITableView and remove the corresponding object in the NSArray, what remains is indexPath.row 1 and 2, but the the indexes for the NSArray are 0 and 1.

If I try to delete what is now the second row from the UITableView, I run into a problem because it's indexPath.row is 2, but the corresponding object in the NSArray has index 1.

To put it another way, the indexPath.row and object indexes are no longer "in sync".

People have suggested using UITableView's reloadData method... This, in fact, does "reset" the indexPaths, but interrupts the UITableView row animation.

How can I do a row delete, update the indexPaths and still maintain the UITableView's animations?!

Upvotes: 4

Views: 9833

Answers (4)

sbauch
sbauch

Reputation: 830

I had a similar issue where I was assigning gesture recognizers to a cell and using the indexPath in the gesture recognizer to interact with the data source.

Instead of using the indexPath provided by tableview:cellForRowAtIndexPath: when constructing the gesture recognizer, I instead get the indexPath provided by cellForRowAtIndexPath: method on the tableView. This approach provided the updated indexPath for a cell even if cells were added or deleted.

Upvotes: 0

Danilo Gomes
Danilo Gomes

Reputation: 757

Please, note my answer in this another thread.

UITableView delete and reload cells

I had some problems related to it. Using fixed index probably you will face a lot of headache. I've solved this adding to the UITableViewCell a reference to a Item from my datasource, not a NSIndexPath. So, there is no need to use indexPath saved into your cell.

Also, there is some information on Apple's Website saying we mustn't use the method reloadData inside insert or delete methods.

Apple docs

all this method to reload all the data that is used to construct the table, including cells, section headers and footers, index arrays, and so on. For efficiency, the table view redisplays only those rows that are visible. It adjusts offsets if the table shrinks as a result of the reload. The table view's delegate or data source calls this method when it wants the table view to completely reload its data. It should not be called in the methods that insert or delete rows, especially within an animation block implemented with calls to beginUpdates and endUpdates

Upvotes: 0

batkuip
batkuip

Reputation: 1478

Have a look how NSFetchedResultsControllerDelegate normally handles it: http://developer.apple.com/library/ios/#documentation/CoreData/Reference/NSFetchedResultsControllerDelegate_Protocol/Reference/Reference.html

Basically all you need to do to remove a cell from the tableview is call [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]

If you have a number changes to make wrap them with [tableView beginUpdates] and [tableView endUpdates] to trigger all the animations at once.

If this still causes an out of sync issue, just use the old trick of deleting the last indexPath first and work your way up. That way all the intermediate indexPaths stay valid.

Upvotes: 2

JeffN
JeffN

Reputation: 1605

Not tested, but a couple things I can think of are either delay the reload until the animations complete. Or just reload those rows rather than the whole table using:

- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation

Delay:

[tableView performSelector:@selector(reloadData) withObject:nil afterDelay:0.5];

Upvotes: 4

Related Questions