Re-arrange Rows in UITableView; Save to Core Data - ios

I have created a list of items in the UITableView, in which, after editing, like re-arranging the rows, when clicked on Done, the editing will stop and will show the changes.
When I leave the app and comes back again when re-launching the application, it should show the changed rows that has been changed previously, before closing application.
Also, I am actually using Core Data and wondering what is needed to it.
EXAMPLE (BEFORE):
A
B
C
EXAMPLE (AFTER RE-ARRANGED):
B
C
A
SHOULD APPEAR WHEN APP IS CLOSED AND LAUNCH:
B
C
A
HOWEVER, IT SHOWS AS (LAST SAVED DATA):
A
B
C
Here are my following codes:
-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
-(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
NSString *stringToMove = self.modules[sourceIndexPath.row];
[self.modules removeObjectAtIndex:sourceIndexPath.row];
[self.modules insertObject:stringToMove atIndex:destinationIndexPath.row];
}
-(IBAction)btnEdit:(UIBarButtonItem *)sender {
UIBarButtonItem * button = ((UIBarButtonItem*)sender);
if (!self.tableView.editing)
{
[self.tableView setEditing:YES animated:YES];
[button setTitle:#"Done"];
}
else
{
[button setTitle:#"Edit"];
[self.tableView setEditing:NO animated:YES];
}
}
THANKS!

Assuming that self.modules are your items stored in core data, adding an order field would allow you to persist the order in which you can retrieve your objects later.
You would need to load your objects ordered by this new field then update it when user rearrange the order in editing mode.

I guess you need to check where self.modules is getting set.
Is the change you are making in the sequence persisted?
If no, then this can only be achieved by - State Preservation and Restoration.

Related

moveRowAtIndexPath - save order number to Core Data blocks UI

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.

How to Save UITableViewController cells after rearrange to NSUserDefaults

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].

How to leave UITableViewCellEditingStyleDelete mode when user edit it with swipe gesture

I need to leave delete mode after user tapped Delete button. I want to show some activity indicator and wait the server response on delete action before I actually remove the cell (or not if the server does not respond). This action I want to perform from delegate method:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
How can I do it?
To hide the "Delete"-Button, you need to use the setEditing:animated: method.
However, your implementation of tableView:commitEditingStyle:forRowAtIndexPath: needs to execute this with a slight delay. The note in the reference Table View Programming Guide for iOS underneath Figure 7-1 states:
Note: The data source should not call setEditing:animated: from within its implementation of tableView:commitEditingStyle:forRowAtIndexPath:. If for some reason it must, it should invoke it after a delay by using the performSelector:withObject:afterDelay: method.
So your implementation could be like this:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// remove delete button only after short delay
[self performSelector:#selector(hideDeleteButton:) withObject:nil afterDelay:0.1];
}
}
- (void)hideDeleteButton:(id)obj
{
[self.tableView setEditing:NO animated:YES];
}
The above code makes the Delete-Button slide away after 0.1 seconds when the user presses it. You will then, of course, need to prevent it going back into the editing mode while waiting for the action to complete. For that, you can override the UITableViewDataSource method tableView:canEditRowAtIndexPath: to prevent the same cell or the whole table to be edited again while waiting.
In my code I make a call to
[self.tableView reloadData];
I'm not sure if this is the 100% correct way to do it but it works for me. So your function could be something like:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
// Do your processing.
[self.tableView reloadData];
}
It should then clear out the red Delete Button and refresh your data.
You may also be able to play around with the call
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
but in my experience its the reloadData call that seems to do the trick
Hi Let me get it straight: Your datasource is actually online and you want confirmation that it is deleted before you update your tableview - in the mean time you want to display an AI Activity indicator.
If that is what you want to do then you start with this method
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
// Get the key of the data you want to delete.
data_struct_instance * data = [self.mutableArray_data_source objectForRowAtIndexPath:indexPath];
NSString *data_key = data.key;
row_index = indexPath.row; // set a property to preserve the row (or index path for multilevel data) for use when you delete the the record later.
[self start_UIActivityIndicator];
[self callonline_with_delete_command:data_key]; // send the request to delete the record
}
once the server responds depending on whether it is successful or not you can either delete the record or reload the entire array if the table is small this is preferable to ensure that the data is synced -
....
[activityindicator StopAnimating];
if (success) {
[self.mutableArray_data_source removeObjectAtIndex:row_index];}
else {
NSLog ....
}
[self.tableView reloadData]; // resets the delete button.

Change UITableView delete button text after it's first shown (in editing mode)

I would like to change the delete text displayed by a UITableView once editing mode has begun.
The delegate method:
-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
is called only when the deleteButton at index path is displayed for the first time, but if my model changes beneath it I need to update this text. Is it possible to cause this method to be called again without reloading the entire section? See code below, and thank you for your help in advance.
-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath {
ContainerTableViewCell *cell = (ContainerTableViewCell*)[self.tableView cellForRowAtIndexPath:indexPath];
if ([cell.editPhotos count] > 0) {
return [NSString stringWithFormat:#"Delete %d photos", [cell.editPhotos count]];
}
else{
return #"Delete Section";
}
}
For a bit of context I have a UICollectionView nested within a UITableViewCell, a notification is sent when a cell is selected. I have tried reloading the section with:
[self.tableView reloadRowsAtIndexPaths:#[[self.tableView indexPathForCell:cell]] withRowAnimation:UITableViewRowAnimationNone];
but this is undesirerable because it causes a jump in the tableview and does not display the selection correctly. I have also tried:
[self.tableView.delegate tableView:self.tableView titleForDeleteConfirmationButtonForRowAtIndexPath:[self.tableView indexPathForCell:cell]];
in desperation. While this does cause the correct method to be called it does not change the delete text.
I just wrote a rudimentary test app where it works as expected.
I think maybe the way you get your data is not the best approach. You are querying a cell that is presumably dequeued and thus might not contain the most up-to-date information.
Instead, you should strive to achieve a true MVC pattern where your data is independent from your views, including collection view cells.
I found a solution to this problem although it is a bit of a hack. The delegate method
-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
is called once for each cell every time the UITableView enters edit mode. Therefore in order to have the title change when the data changes I toggled the edit mode, using a bool to indicate that I wished to save selected information ie:
cell.retainEditSelection = YES;
[self.tableView setEditing:NO animated:NO];
[self.tableView setEditing:YES animated:NO];
cell.retainEditSelection = NO;
I use this every time something is selected that should change my delete text. Hope this helps .

Core Data - Reordering TableView won't save

.
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.

Resources