Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing the Sorting in an NSFetchedResultsController on the fly

I'm trying to change the sorting in a NSFetchController on the fly, by some sort of segmented control. To either sort A->Z Z->A type thing.

What do I have to do to do this? I'm following Jeff Lamarche's example here: Here

Do I need to make a new NSFetchedResultsController and then set it, or do I just make a new NSFetchRequest and do

fetchedResultController.fetchRequest = newFetchRequest

and then my table will automatically update?

like image 211
aleclerc Avatar asked Nov 11 '09 16:11

aleclerc


3 Answers

I was stuck with the same problem and I could fix it with just setting the sort descriptors on the FetchRequestController's FetchRequest, then execute a fetch (performFetch) and finally reload the data on the table view.

NSArray *sortDescriptors = [NSArray arrayWithObject: sorter];
[[fetchedResultsController fetchRequest] setSortDescriptors:sortDescriptors];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
        // Handle you error here
}
[sorter release];
[myTableView reloadData];

This was the easiest way for me to deal with the change the sorting on the fly without dropping the FRC or do any other fancy object handling. I am not sure about the performance implications on that and it could have an impact if the set of rows in the table is huge.

like image 168
Andreas Schaefer Avatar answered Sep 28 '22 03:09

Andreas Schaefer


Instead of using your NSFetchedResultsController as your table view data source, create an NSArray that you set when the user changes sort order with your segmented control basing the array contents on the fetched results. Then just sort using standard array sorting. Something like this:

- (IBAction)segmentChanged:(id)sender
{
    // Determine which segment is selected and then set this 
    // variable accordingly
    BOOL ascending = ([sender selectedSegmentIndex] == 0);

    NSArray *allObjects = [fetchedResultsController fetchedObjects];

    NSSortDescriptor *sortNameDescriptor = 
                       [[[NSSortDescriptor alloc] initWithKey:@"name" 
                                 ascending:ascending] autorelease];

    NSArray *sortDescriptors = [[[NSArray alloc] 
                     initWithObjects:sortNameDescriptor, nil] autorelease];

    // items is a synthesized ivar that we use as the table view
    // data source.
    [self setItems:[allObjects sortedArrayUsingDescriptors:sortDescriptors]];

    // Tell the tableview to reload.
    [itemsTableView reloadData];    
}

So the sort descriptor I've used is called "name", but you would change this to the name of the field you want to sort by in the fetched results. Also, the items ivar I've referenced would be your new table view data source. Your table view delegates would now be something like this:

- (NSInteger)tableView:(UITableView*)tableView 
 numberOfRowsInSection:(NSInteger)section
{
    return [items count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Get your table cell by reuse identifier as usual and then grab one of
    // your records based on the index path
    // ...

    MyManagedObject *object = [items objectAtIndex:[indexPath row]];

    // Set your cell label text or whatever you want 
    // with one of the managed object's fields.
    // ...

    return cell;
}

Not sure if this is the best way, but it should work.

like image 40
Matt Long Avatar answered Sep 28 '22 04:09

Matt Long


Andreas Schaefer answer worked for me. The trick is setting a new parameter for the fetch request with whether it is this code:

[[fetchedResultsController fetchRequest] setSortDescriptors:sortDescriptors];

OR

[[fetchedResultsController fetchRequest] setPredicate:predicate];

Before calling this:

if (![[self fetchedResultsController] performFetch:&error]) {
        // Handle you error here
}

[self.tableView reloadData];

it appears that when you set a new parameter, than the NSFetchedResultsController will perform a new fetchrequest based upon what you set.. Without setting a new parameter, it won't perform the re-fetch and just calling performFetch it won't work..

Hope this helps someone else..

like image 21
sudo Avatar answered Sep 28 '22 02:09

sudo