Difference of 2 NSArray's for animated insert/delete in UITableView - cocoa-touch

At some point in my Application, I have an NSArray whose contents change. Those contents are shown in a UITableView. I'm trying to find a way to find the diff between the contents of before and after of the NSArray so i can pass the correct indexPaths to insertRowsAtIndexPaths:withRowAnimation: and deleteRowsAtIndexPaths:withRowAnimation: in order to have the changes nicely animated. Any ideas?
thx

Here's is wat i tried and it seems to work, if anyone has anything better, i'd love to see it.
[self.tableView beginUpdates];
NSMutableArray* rowsToDelete = [NSMutableArray array];
NSMutableArray* rowsToInsert = [NSMutableArray array];
for ( NSInteger i = 0; i < oldEntries.count; i++ )
{
FDEntry* entry = [oldEntries objectAtIndex:i];
if ( ! [displayEntries containsObject:entry] )
[rowsToDelete addObject: [NSIndexPath indexPathForRow:i inSection:0]];
}
for ( NSInteger i = 0; i < displayEntries.count; i++ )
{
FDEntry* entry = [displayEntries objectAtIndex:i];
if ( ! [oldEntries containsObject:entry] )
[rowsToInsert addObject: [NSIndexPath indexPathForRow:i inSection:0]];
}
[self.tableView deleteRowsAtIndexPaths:rowsToDelete withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths:rowsToInsert withRowAnimation:UITableViewRowAnimationRight];
[self.tableView endUpdates];

This question from 2010 is what I found when I was googling. Since iOS 5.0, we now also have -[UITableView moveRowAtIndexPath:toIndexPath] which you really want to handle as well. Here is a function that compares two arrays and generates suitable indexpaths for the delete, insert and move operations.
- (void) calculateTableViewChangesBetweenOldArray:(NSArray *)oldObjects
newArray:(NSArray *)newObjects
sectionIndex:(NSInteger)section
indexPathsToDelete:(NSArray **)indexPathsToDelete
indexPathsToInsert:(NSArray **)indexPathsToInsert
indexPathsToMove:(NSArray **)indexPathsToMove
destinationIndexPaths:(NSArray **)destinationIndexPaths
{
NSMutableArray *pathsToDelete = [NSMutableArray new];
NSMutableArray *pathsToInsert = [NSMutableArray new];
NSMutableArray *pathsToMove = [NSMutableArray new];
NSMutableArray *destinationPaths = [NSMutableArray new];
// Deletes and moves
for (NSInteger oldIndex = 0; oldIndex < oldObjects.count; oldIndex++) {
NSObject *object = oldObjects[oldIndex];
NSInteger newIndex = [newObjects indexOfObject:object];
if (newIndex == NSNotFound) {
[pathsToDelete addObject:[NSIndexPath indexPathForRow:oldIndex inSection:section]];
} else if (newIndex != oldIndex) {
[pathsToMove addObject:[NSIndexPath indexPathForRow:oldIndex inSection:section]];
[destinationPaths addObject:[NSIndexPath indexPathForRow:newIndex inSection:section]];
}
}
// Inserts
for (NSInteger newIndex = 0; newIndex < newObjects.count; newIndex++) {
NSObject *object = newObjects[newIndex];
if (![oldObjects containsObject:object]) {
[pathsToInsert addObject:[NSIndexPath indexPathForRow:newIndex inSection:section]];
}
}
if (indexPathsToDelete) *indexPathsToDelete = [pathsToDelete copy];
if (indexPathsToInsert) *indexPathsToInsert = [pathsToInsert copy];
if (indexPathsToMove) *indexPathsToMove = [pathsToMove copy];
if (destinationIndexPaths) *destinationIndexPaths = [destinationPaths copy];
}
An example on how to use it. Assume you're displaying a table of people, which you keep in the array self.people. The section index where the people are displayed is 0.
- (void) setPeople:(NSArray <Person *> *)newPeople {
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView beginUpdates];
NSArray *rowsToDelete, *rowsToInsert, *rowsToMove, *destinationRows;
[self calculateTableViewChangesBetweenOldArray:self.people
newArray:newPeople
sectionIndex:0
indexPathsToDelete:&rowsToDelete
indexPathsToInsert:&rowsToInsert
indexPathsToMove:&rowsToMove
destinationIndexPaths:&destinationRows
];
self.people = newPeople;
[self.tableView deleteRowsAtIndexPaths:rowsToDelete withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths:rowsToInsert withRowAnimation:UITableViewRowAnimationFade];
[rowsToMove enumerateObjectsUsingBlock:^(NSIndexPath * _Nonnull oldIndexPath, NSUInteger idx, BOOL * _Nonnull stop) {
NSIndexPath *newIndexPath = destinationRows[idx];
[self.tableView moveRowAtIndexPath:oldIndexPath toIndexPath:newIndexPath];
}];
[self.tableView endUpdates];
});
}

Related

How to form an array of dictionaries for multilevel(any number) tableview from the core data object

I need to create array of dictionaries, of data coming from a database object. This array of dictionaries can be of multiple level,and has a parent child relationship depending upon the level.
From the array form I need to create a multilevel table view (level can be any depending upon the data loaded)
Depending upon the catal_id of the coredata object, next set of Catal objects are loaded from the database. Code supporting is shown as below.
I want to create the array as shown in the image
From the above load of loadMainCatalData I am able to load the table but on didSelectRowAtIndexPath I am not able to form the proper array for the expand collapse table. Catal object gets added twice, on viewDidLoad.
There is some issue with function.
**Please help to form the proper array to load the table. Stuck here **.
The coredata object is of the below format:
<__NSArrayM 0x109c3b7a0>(
<Catal: 0x104cccfb0>
(entity: Catal; id: 0xd000000008880006 <x-coredata://F849E220-C905-4359-8CD5-18D5E35FC13A/Catal/p546> ; data: {
breadcrumb = "";
"catal_id" = "SNV2";
"id_ni" = 1;
"id_parent" = 0;
imgId = 1;
title = "Adventure";
"nb_element" = 1010;
order = 38;
}),
<Catal: 0x104ccd3f0> (entity: Catal; id: 0xd000000006e40006 <x-coredata://F849E220-C905-4359-8CD5-18D5E35FC13A/Catal/p441> ; data: {
breadcrumb = "";
"catal_id" = "SNV1";
"id_ni" = 1;
"id_parent" = 0;
imgId = 38;
title = Gros;
"nb_element" = 1366;
order = 82;
}),
<Catal: 0x104ccd6e0> (entity: Catal; id: 0xd00000000a500006 <x-coredata://F849E220-C905-4359-8CD5-18D5E35FC13A/Catal/p660> ; data: <fault>),
<Catal: 0x104ccd790> (entity: Catal; id: 0xd000000005d40006 <x-coredata://F849E220-C905-4359-8CD5-18D5E35FC13A/Catal/p373> ; data: <fault>),
<Catal: 0x104ccd940> (entity: Catal; id: 0xd00000000acc0006 <x-coredata://F849E220-C905-4359-8CD5-18D5E35FC13A/Catal/p691> ; data: <fault>)
)
My code goes as below
- (void)viewDidLoad {
[super viewDidLoad];
if (!self.catalList || self.catalList.count == 0) {
[self loadDataCatal];
isAlreadyInserted = NO;
}
}
- (void)loadDataCatal{
[self loadMainCatalData];
self.arForTable = [NSMutableArray array];
[self.arForTable addObjectsFromArray:self.arrayOriginal];
}
-(void)loadMainCatalData {
NSMutableArray *arrCatalList = [[NSMutableArray alloc] init];
if (catLevel == NULL){
[arrCatalList addObjectsFromArray:[Catal fillDataCatal:#"0" :#"0"]];
}
self.arrayOriginal = [NSMutableArray array];
for (Catal *objCatal in arrCatalList){
ProductCategoryFilter *objProductCatFilter = [[ProductCategoryFilter alloc] init];
[objProductCatFilter setCatalCategory:objCatal];
NSMutableArray *arr = [self loadSubCatalData:objCatal];
[objProductCatFilter setArrCatalSubCategory:arr];
[self.arrayOriginal addObject:objProductCatFilter];
}
}
-(NSMutableArray *)loadSubCatalData:(Catal *)objCatal{
NSMutableArray *arrSubCatal = [NSMutableArray array];
ProductCategoryFilter *objProductCatFilter = [[ProductCategoryFilter alloc] init];
[objProductCatFilter setCatalCategory:objCatal];
NSArray *arrCatal = [Catal fillDataCatal:objCatal.catal_id :#""];
NSMutableArray *arrSubCat = [NSMutableArray array];
for (Catal *subCatal in arrCatal){
ProductCategoryFilter *objSubCatFilter = [[ProductCategoryFilter alloc] init];
[objSubCatFilter setCatalCategory:subCatal];
NSMutableArray *arr = [self loadSubCatalData:subCatal];
[objSubCatFilter setArrCatalSubCategory:arr];
[arrSubCat addObject:objSubCatFilter];
}
[objProductCatFilter setArrCatalSubCategory:arrSubCat];
[arrSubCatal addObject:objProductCatFilter];
return arrSubCatal;
}
// the ProductCategoryFilter class
#import <Foundation/Foundation.h>
#import "Catal+CoreDataClass.h"
#interface ProductCategoryFilter : NSObject
#property (nonatomic,strong) Catal* catalCategory;
#property (nonatomic,strong) NSMutableArray * arrCatalSubCategory;
#end
// Catal CoreDataObject
#import <Foundation/Foundation.h>
#import "Catal+CoreDataClass.h"
+(BOOL)fillSubDataCatal:(NSString *)catal_id
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *context = [[CoreDataHelper getInstance] managedObjectContext];
NSError *error;
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Catal" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate;
predicate = [NSPredicate predicateWithFormat:#"id_parent == %#", catal_id];
[fetchRequest setPredicate:predicate];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
DebugLog(#"[fetchedObjects count] : %lu",(unsigned long)fetchedObjects.count);
if([fetchedObjects count] > 0)
{
return true;
}
return false;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier =#"AMG_PP_SubCategoryTableCell";
AMG_PP_SubCategoryTableCell *cell = (AMG_PP_SubCategoryTableCell *) [self.tblProductCategory dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil){
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
cell =(AMG_PP_SubCategoryTableCell *)[nib objectAtIndex:0];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
ProductCategoryFilter *objCat = [self.arForTable objectAtIndex:indexPath.row];
[cell.imgRadio setImage:[UIImage imageNamed:#"PlusIcon"]];
cell.textLabel.text = objCat.catalCategory.libelle; //] [ valueForKey:#"name"];
// [cell setIndentationLevel:[[[self.arForTable objectAtIndex:indexPath.row] valueForKey:#"level"] intValue]];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
AMG_PP_SubCategoryTableCell *cell = (AMG_PP_SubCategoryTableCell *)[self.tblProductCategory cellForRowAtIndexPath:indexPath];
[self.tblProductCategory deselectRowAtIndexPath:indexPath animated:YES];
ProductCategoryFilter *objCatal = [self.arForTable objectAtIndex:indexPath.row];
if([[objCatal arrCatalSubCategory] count] > 0) {
NSMutableArray *ar=[objCatal arrCatalSubCategory];
if(ar != nil){
isAlreadyInserted=NO;
for(ProductCategoryFilter *dInner in ar ){
NSInteger index=[self.arForTable indexOfObjectIdenticalTo:dInner];
isAlreadyInserted=(index>0 && index!=NSIntegerMax);
if(isAlreadyInserted) break;
}
if(isAlreadyInserted) {
[self miniMizeThisRows:ar];
} else {
NSUInteger count=indexPath.row+1;
NSMutableArray *arCells=[NSMutableArray array];
for(ProductCategoryFilter *dInner in ar ){
[arCells addObject:[NSIndexPath indexPathForRow:count inSection:0]];
[self.arForTable insertObject:dInner atIndex:count++];
}
[cell.imgRadio setImage:[UIImage imageNamed:#"MinusIcon"]];
[tableView insertRowsAtIndexPaths:arCells withRowAnimation:UITableViewRowAnimationLeft];
}
}
}
}
-(void)miniMizeThisRows:(NSArray*)ar{
for(ProductCategoryFilter *dInner in ar ){
NSUInteger indexToRemove=[self.arForTable indexOfObjectIdenticalTo:dInner];
NSMutableArray *arInner=[dInner arrCatalSubCategory];
if(arInner && [arInner count]>0){
if (arInner != nil) {
[self miniMizeThisRows:arInner];
}
}
if([self.arForTable indexOfObjectIdenticalTo:dInner]!=NSNotFound) {
[self.arForTable removeObjectIdenticalTo:dInner];
[self.tblProductCategory deleteRowsAtIndexPaths:[NSArray arrayWithObject:
[NSIndexPath indexPathForRow:indexToRemove inSection:0]
]
withRowAnimation:UITableViewRowAnimationRight];
}
}
}
Here's the simple code which help you in understanding the how to create a array of dictionary :
var arrayOfDict = [[String : String]]()
let dict1 = ["FirstName" : "Abc" , "LastName" : "XYZ"]
let dict2 = ["HouseNo" : "WW49", "Locality" : "GymKhana"]
let dict3 = ["City" : "mnb", "State" : "lkop" , "Country" : "mkl"]
arrayOfDict.append(dict1)
arrayOfDict.append(dict2)
arrayOfDict.append(dict3)
print(arrayOfDict)

Table View Insert error

Hi guys i get this error when i try to add to a empty table view : insert row 0 into section 0, but there are only 0 rows in section 0 after the update. Any thoughts?
dispatch_queue_t fetchQ = dispatch_queue_create("Blog Fetcher", NULL);
dispatch_async(fetchQ, ^{
//pull this call out to Blog+twitter later saving should only take
//place in the model!
[document.managedObjectContext performBlock:^
{
NSArray* resultsArray = [self.tweets objectForKey:#"results"];
for (NSDictionary* internalDict in resultsArray)
{
User *user = [NSEntityDescription
insertNewObjectForEntityForName:#"User"inManagedObjectContext:self.context];
user.username =[internalDict objectForKey:#"from_user_name"];
[self.context save:nil];
self.list = [self.list arrayByAddingObject:user];
NSLog(#" indexpath set here %i",self.list.count-1);
NSIndexPath *newIndexpath =[NSIndexPath indexPathForRow:self.list.count-1 inSection:0];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexpath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}];
});
dispatch_release(fetchQ);
}
My first thought is that you should dispatch updates to the main thread. I'm not sure about your implementation details, but you may also need to move [self.context save:nil] to the main thread also.
NSArray* resultsArray = [self.tweets objectForKey:#"results"];
NSMutableArray *users = [NSMutableArray arrayWithCapacity:resultsArray.count];
NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:resultsArray.count];
NSInteger row = self.list.count;
for (NSDictionary* internalDict in resultsArray)
{
User *user = [NSEntityDescription insertNewObjectForEntityForName:#"User"inManagedObjectContext:self.context];
user.username =[internalDict objectForKey:#"from_user_name"];
[self.context save:nil];
[users addObject:user];
NSLog(#" indexpath set here %i", row);
NSIndexPath *newIndexpath =[NSIndexPath indexPathForRow:row inSection:0];
row++;
[indexPaths addObject:newIndexpath];
}
dispatch_async(dispatch_get_main_queue(), ^{
self.list = [self.list arrayByAddingObjectsFromArray:users];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexpath] withRowAnimation:UITableViewRowAnimationAutomatic];
};

NSDictionary in NSArray search

I'm having a problem with some search code. I have an NSMutableArray, called searchedData, that contains NSDictionaries (one per object). Here's the search code I have now:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[tableData removeAllObjects]; // remove all data that belongs to previous search
if([searchText isEqualToString:#""]||searchText==nil) {
[tableView reloadData];
return;
}
int i = 0;
while (i < [dataSource count]) {
NSDictionary *coolDict = [searchedData objectAtIndex:i];
NSString * title = [coolDict objectForKey:#"TITLE"];
NSString * authorString = [coolDict objectForKey:#"AUTHOR"];
NSRange titleRange = [[title lowercaseString] rangeOfString:[searchText lowercaseString]];
NSRange authorRange = [[authorString lowercaseString] rangeOfString:[searchText lowercaseString]];
if (titleRange.location != NSNotFound || authorRange.location != NSNotFound)
[tableData addObject:title];
i++;
}
[tableView reloadData];
}
It finds the number of entries, then goes to each entry, finds the objects for keys "TITLE" and "AUTHOR", and then displays entries in the UITableView if there is a match.
The problem is that it never displays anything, even if there is a match.
I know the table / dictionaries are not null (I have NSLogged it) so I don't know why it's not working.
You forgot to reload the tableView having added the relevant data to the datasource.
I have improved the coding of the method a bit, and added the missing line.
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[tableData removeAllObjects];// remove all data that belongs to previous search
if (![searchText isEqualToString:#""] || searchText != nil) {
int i = 0;
while (i < [dataSource count]) {
NSDictionary *coolDict = [searchedData objectAtIndex:i];
NSString * title = [coolDict objectForKey:#"TITLE"];
NSString * authorString = [coolDict objectForKey:#"AUTHOR"];
NSRange titleRange = [[title lowercaseString] rangeOfString:[searchText lowercaseString]];
NSRange authorRange = [[authorString lowercaseString] rangeOfString:[searchText lowercaseString]];
if(titleRange.location != NSNotFound || authorRange.location != NSNotFound)
[tableData addObject:title];
i++;
}
}
[tableView reloadData];
}

cancel button on search bar does not cancel correctly

I have a search bar, i can search now, but when I enter a text to search, and click the cancel button. It does not give me back my first stage, meaning full of the items in the table.
For example: I search the item with word: a, it gives me all the a items, yes, it is right now, but when i hit the cancel button, i want the programme gives me all the items exist, not just a items.
Here is the code: please help me out. Thank you so much.
- (void)searchBarCancelButtonClicked:(UISearchBar *)aSearchBar
{
searchBar.text = #"";
[searchBar resignFirstResponder];
letUserSelectRow = YES;
searching = NO;
self.tableView.scrollEnabled = YES;
NSLog(#"what text after cancel now: %#", searchBar.text);
[self.tableView reloadData];
}
- (NSMutableArray *) searchTableView {
NSString *searchText = searchBar.text;
NSLog(#"search text: %#", searchText);
NSMutableArray *resultArray = [[NSMutableArray alloc] init];
NSMutableArray *tempArr = [[NSMutableArray alloc] init];
for (NSDictionary *dTemp in arrayData)
{
NSString *tempStr = [dTemp objectForKey:#"url"];
NSLog(#"sTemp string: %#",[ NSString stringWithFormat:#"%#", tempStr]);
NSRange titleResultsRange = [tempStr rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0)
{
NSLog(#"1 count :%d", [resultArray count]);
[resultArray addObject:dTemp];
NSLog(#"2 count :%d", [resultArray count]);
[tempArr addObject:resultArray];
[resultArray release];
resultArray = [NSMutableArray new];
}
}
if (resultArray != nil) {
[resultArray release];
}
return tempArr;
}
- (void)searchBar:(UISearchBar *)aSearchBar textDidChange:(NSString *)searchText
{
NSLog(#"what text after cancel now: %#", searchBar.text);
if([searchText length] > 0) {
[sortedArray removeAllObjects];
searching = YES;
letUserSelectRow = YES;
self.tableView.scrollEnabled = YES;
NSMutableArray *searchArray = [self searchTableView];
sortedArray = [[NSMutableArray alloc] initWithArray:searchArray copyItems:YES];
for (int i = 0; i<[sortedArray count]; i++) {
NSLog(#"this is the search array: %#", [[sortedArray objectAtIndex:i] class]);
}
NSLog(#"sorted array: %d", [sortedArray count]);
}
else {
searching = NO;
letUserSelectRow = NO;
self.tableView.scrollEnabled = NO;
}
[self.tableView reloadData];
}
You don't need to override any of UISearchBar methods to accomplish this. The new way of doing this relies on the UISearchDisplay controller instead (specifically on shouldReloadTableForSearchString).
Declare your view controller to conform to UISearchDisplayDelegate protocol, and keep two instance variables: your model as NSArray (all data) and a filtered array as NSMutableArray (a subset of your data). The code you presently have in "searchTableView" would filter the content of the model and place it into the filtered NSMutableArray. Then you would override the following UITableView methods: -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section and -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath. In each, before returning, make a comparison to determine whether your tableView argument is equal to self.searchDisplayController.searchResultsTableView. If it is, the user is looking at the filtered list and your should use the content of the filtered NSMutableArray to create the view, otherwise, the user is looking at the whole data set and you should use the content of the NSArray that holds your model. Take a look at the following Apple code for a simple example of what I described:
http://developer.apple.com/library/ios/#samplecode/TableSearch/Introduction/Intro.html

View-based NSTableView filtering + animation

I have a view based NSTableView that I sometimes filter using NSPredicate. Is there any way to animate the items being removed/added/reordered throughout the tableview to have the same effect as beginUpdates, endUpdates and insertRowsAtIndexes:withAnimation, etc?
I've explored ways such as manually filtering out my array but my attempts proved to be futile so now I am wondering if there is a better (or built in way) to do this. I have wondered if NSArrayController does this automatically but I don't think it does.
I've written code to do this myself - given 'before' and 'after' arrays, compute the required parameters to insertRowsAtIndexPaths:, deleteRowsAtIndexPaths:, etc. The code is a bit fiddly so probably has bugs - use at your discretion!
#interface NSArray (ArrayDifference)
- (void) computeDifferenceTo:(NSArray *)newArray returningAdded:(NSMutableArray **)rowsAdded andDeleted:(NSMutableArray **)rowsDeleted;
#end
#implementation NSArray (ArrayDifference)
// Given two arrays that are expected have items added or removed but not re-ordered, compute the differences
// in a way usable for UITable insertRows and deleteRows
- (void) computeDifferenceTo:(NSArray *)newArray returningAdded:(NSMutableArray **)rowsAdded andDeleted:(NSMutableArray **)rowsDeleted
{
NSArray *oldArray = self;
*rowsAdded = [[[NSMutableArray alloc] init] autorelease];
*rowsDeleted = [[[NSMutableArray alloc] init] autorelease];
NSUInteger oldCount = [oldArray count];
NSUInteger newCount = [newArray count];
// Step through the two arrays
NSInteger oldIndex = 0, newIndex=0;
for (; newIndex < newCount && oldIndex < oldCount; )
{
id newItem = [newArray objectAtIndex:newIndex];
id oldItem = [oldArray objectAtIndex:oldIndex];
// If the two objects match, we step forward on both sides
if (newItem == oldItem) {
++newIndex;
++oldIndex;
}
else {
// Look for the old item to appear later in the new array, which would mean we have to add the rows in between
NSRange range = { newIndex+1, newCount - newIndex-1 };
NSUInteger foundIndex = [newArray indexOfObject:oldItem inRange:range];
if (foundIndex != NSNotFound)
for (; newIndex < foundIndex; ++newIndex)
[*rowsAdded addObject:[NSIndexPath indexPathForRow:newIndex inSection:0]];
else {
// Look for the new item to appear later in the old array, which would mean we have to remove the rows in between
NSRange range = { oldIndex+1, oldCount - oldIndex-1 };
NSUInteger foundIndex = [oldArray indexOfObject:newItem inRange:range];
if (foundIndex != NSNotFound)
for (; oldIndex < foundIndex; ++oldIndex)
[*rowsDeleted addObject:[NSIndexPath indexPathForRow:oldIndex inSection:0]];
else {
// Old item must be removed and new item added, then we carry on
[*rowsAdded addObject:[NSIndexPath indexPathForRow:newIndex++ inSection:0]];
[*rowsDeleted addObject:[NSIndexPath indexPathForRow:oldIndex++ inSection:0]];
}
}
}
}
// Once the loop is finished, add in what's left in the new array and remove what is left in the old array
for (; newIndex < newCount; ++newIndex)
[*rowsAdded addObject:[NSIndexPath indexPathForRow:newIndex inSection:0]];
for (; oldIndex < oldCount; ++oldIndex)
[*rowsDeleted addObject:[NSIndexPath indexPathForRow:oldIndex inSection:0]];
}
#end
Then you call it like this:
NSMutableArray *rowsAdded=nil, *rowsDeleted=nil;
[myArray computeDifferenceTo:newArray returningAdded:&rowsAdded andDeleted:&rowsDeleted];
[myTableView beginUpdates];
[myTableView insertRowsAtIndexPaths:rowsAdded withRowAnimation:UITableViewRowAnimationBottom];
[myTableView deleteRowsAtIndexPaths:rowsDeleted withRowAnimation:UITableViewRowAnimationFade];
[myTableView endUpdates];