The code below implements drag and drop functionality in a table view. As you can see it accomplishes reordering by removing the object from an array (dataList) at a certain index and then positions it at the desired index. This code doesn't use core data. My question is, how would you achieve this using core data. For example, would you continue using an array as in the code below and then save the array to core data and you try to retrieve and order the objects from core data according to the order in the array. Or would you reorder the objects directly in the core data store? if the latter, how?
#pragma mark Row reordering
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
toIndexPath:(NSIndexPath *)toIndexPath {
NSString *item = [[dataList objectAtIndex:fromIndexPath.row] retain];
[dataList removeObject:item];
[dataList insertObject:item atIndex:toIndexPath.row];
[item release];
}
I've implemented a similar behavior by storing the order information in the coredata object. Let's call it position.
By this you are able to pass all your objects as an NSArray ordered by NSSortDescriptor with the property position.
After a drag&drop happened, make sure you update the position properties of the changed items accordingly.
Related
in my app I use a UITableView that is filled with Core Data objects. I wanted to make it possible to change to order of the cells. So I implemented
-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
and
-(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
I am saving an order number for every object to Core Data to use it for the sort descriptor to make the reordering persistant. So I am doing
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
NSMutableArray *allFRCObjects = [[self.fetchedResultsController fetchedObjects] mutableCopy];
NSString *stringToMove = [allFRCObjects objectAtIndex:sourceIndexPath.row];
[allFRCObjects removeObjectAtIndex:sourceIndexPath.row];
[allFRCObjects insertObject:stringToMove atIndex:destinationIndexPath.row];
NSUInteger countInt = [allFRCObjects count];
float f = (float) countInt;
f = f -1;
for (NSManagedObject *mo in allFRCObjects) {
[mo setValue:[NSNumber numberWithFloat:f] forKey:#"orderNumberFloat"];
f--;
}
}
This is working in the simulator, however if I test on an old iPad 3 with many Objects in the TableView the UI gets blocked for some seconds and obviously
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
is getting called many times what is causing the blocking of the UI I guess...
I am not that pro with these async things and so. Did a bit try and error with dispatch but it didn t help.
What could I do about it ?
Thanks for help !
EDIT
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
case NSFetchedResultsChangeUpdate:
is getting called too often from my For-Loop in
moveRowAtIndexPath:
I wonder if I can avoid this !?
Thanks
Start with running Instruments and using the Time Profiler. Once you can see the block in Instruments turn off system libraries and invert the call stack. That will show you what piece of code is costing the time.
Most likely it is the population or drawing of your UITableViewCell.
Whatever method is showing as taking up the time, double click on that method, it will show the line of code.
That will be the code you need to correct.
Are you reindexing the sort order for all your managed objects with every move? This could also be a bottleneck.
I remember a way of doing this that was shown in the old BNR iOS books that was much more efficient. As you add new objects, increment the sort order by 1. When reordering objects grab the sort order for the objects before and after your new destination index. Then do some math that gives you a float exactly half way between those two sort order numbers and set that as the sort order on your moved object. This way you only ever have to update the moved objects.
I've an iOS application developed using Objective-C & Xcode 6.4
Right know I'm working on manually rearranging the UITableViewController cells, everything working great. But after I press the bar button EDIT and the "3 underlines" appear to drag the cell anywhere I want in the UITableViewController, (( I can't save what I did )). So how could I do a Persistent save the changes done to the table cells location ?? I mean, How to save The new rearranged NSMutableArray to a Property list -NSUserDefaults-.
I'm using a Mutable Array to display the table's cells and these methods below:
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewCellEditingStyleNone;
}
-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
FileWML *fileWML = [self.filesWML objectAtIndex:sourceIndexPath.row];
[self.filesWML removeObjectAtIndex:sourceIndexPath.row];
[self.filesWML insertObject:fileWML atIndex:destinationIndexPath.row];
}
PLUS would someone tell me how I can make the edit button display the word Done, while editing ?
(( I can't save what I did )) means: after I do the rearranging order I want, and then I go to the home view then I get back to the table view I edit and rearrange, the rearranging order I did get back to the default order. So, all the rearranging I did is gone.
Thanks and every help is appreciated.
You have to load your datas from your file in an array. This array will be your tableView dataSource.
When a row is moved, in the delegate method you have to change the object's place in the array. Your array should always be the same as your tableview !
Once the editing of the tableview is done you save the array in the file. The previous content in file should be erased. Then you reload your datas ([tableView reloadData].
I want to delete a row in my table view cell. At the moment, the code will look like this:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
// Remove the row from data model
[listOfAllOpenChats removeObjectAtIndex:indexPath.row];
[tableView reloadData];
}
The problem: I will become an error:
No visible #infterface "NSArray" declares the selector
"removeObjectAtIndex:"
Replace your NSArray listOfAllOpenChats with NSMutableArray,you cannot remote object from NSArray
Your listOfAllOpenChats is probably a NSArray type, which has NOT support to remove or add objects. Change your listOfAllOpenChats array type to NSMutableArray and your problem will be gone.
You can't add or remove objects from an NSArray(immutable). You have to use an NSMutableArray.
I am building an iPhone application that is using a table cell. I set up this code to try to delete cells from the table, but it does not seem to work because I am not using a NSMutableArray. When I swipe I get the delete button but the delete button does not do anything. My question is if I am not using NSMutableArray with my table cell ho can this line: [self.dataArray removeObjectAtIndex:indexPath.row]; work?
Code Used
(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
return YES; }
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
//remove the deleted object from your data source.
//If your data source is an NSMutableArray, do this
[self.dataArray removeObjectAtIndex:indexPath.row];
} }
Here is the link to the apple doc's on table view programming guide. Table view programming guide It has a easy delegate method that uses NSArray and has an example code you can use for deleting and inserting. The code you have shown above is missing the delegate part in order to perform the delete. Take a look at listing 7.3 that maybe what your looking for.
.
Hello,
I have the following code to set the re-ordering of the tableView.
#pragma mark Row reordering
// Determine whether a given row is eligible for reordering or not.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
// Process the row move. This means updating the data model to <span id="IL_AD6" class="IL_AD">correct</span> the item indices.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
toIndexPath:(NSIndexPath *)toIndexPath {
NSString *item = [arr objectAtIndex:fromIndexPath.row];
[arr removeObject:item];
[arr insertObject:item atIndex:toIndexPath.row];
}
But when I reload the TableView (note not a TableViewController) The order is set back to what it was before I changed anything.
Now when I setup the editing for this view (the delete buttons) I had to add this line:
[self.mainTableView setEditing:YES animated:YES];
before it appeared in my view.
Is there a similar thing I need to set with the re-ordering of the table view for it to save?
The data is fetched from a Core Data database.
In order to persist the order of the objects I'm afraid you will need to add an attribute for that to your data model. Just use an int / [NSNumber] and recompute them in a loop when a table view row is moved.
When you reload the data, are you re-populating that array with the result of a fetch from your Core Data entity? If so, the changes you made to the array (which I assume is the data source for the TV) will be blown away. If you want those changes to stick, you would need to update the model somehow to make sure the re-population of the array results in the array being indexed the way you need.