Template for updating UITableView during synchronization?

I am working on an iOS project for reading feeds. Feed entries are listed in a UITableView in reverse chronological order. At startup, they are loaded from the database into an array.

When the application synchronizes with the feeds, it creates a new array for the new order of things, and then updates the table, compares the new array with the old array to determine which cells to delete, update or insert. The way I did this is naive, and therefore very inefficient: there are a lot of calls to indexOfObject: to see if an element is in one array in another array. Twice. once for each new record when it is added to the new array to see if it is in the old array, and then once for each record in the old array to see if it is in the new array.

As a database professional, this design offends me.

But this should be a pretty general template. What would be the most appropriate, Cocoa-like way to do this?

+4
source share
2 answers

It turned out that I was wrong. The solution I found was to add and remove elements from the array, as usual, and then to call insertRowsAtIndexPaths:withRowAnimation: reloadRowsAtIndexPaths:withRowAnimation: and deleteRowsAtIndexPaths:withRowAnimation: depending on each row being added. The error in my previous plan was the idea that I had to wait until all the changes were made, and then each of these methods would be beginUpdates only once in the beginUpdates / endUpdates . It turns out that the block is not really needed, since modification methods can be called outside of them.

It was much easier to call each method once for each cell inserted, updated, or deleted than to compute all changes at the end and commit them immediately. He was too confused, error prone and ineffective to try to do everything at once.

So, the code I finished looks like this:

 if (parsedItem.savedState == ItemModelSavedStateInserted) { // It a new entry. Insert it. [items addObject:parsedItem]; [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:items.count - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationTop]; } else { // It an existing entry. Find it in the portal and move it, if necessary. NSUInteger foundAt = [items indexOfObject:parsedItem inRange:NSMakeRange(currentItemIndex, items.count - currentItemIndex - 1) ]; if (foundAt == currentItemIndex) { // It hasn't moved! if (parsedItem.savedState == ItemModelSavedStateUpdated) { // It was updated, so replace it. [items replaceObjectAtIndex:currentItemIndex withObject:parsedItem]; [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:currentItemIndex inSection:0]] withRowAnimation:UITableViewRowAnimationMiddle]; } } else { // It has shifted position. if (foundAt != NSNotFound) { // It has moved. [items removeObjectAtIndex:foundAt]; [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:foundAt inSection:0]] withRowAnimation:UITableViewRowAnimationBottom]; } // Need to insert it. [items insertObject:parsedItem atIndex:currentItemIndex]; [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:currentItemIndex inSection:0]] withRowAnimation:UITableViewRowAnimationTop]; } } 
+4
source

Consider using NSS for a difference set of current items and a set of new items, with one NSMutableArray to store an ordered current list. You will probably want to remove each of the expired items from the array, and then move each of the unexpired new items to the array. Elements that you do not need to remove or insolate are elements that you can update.

+1
source

All Articles