Search code examples
ioscore-datansfetchedresultscontroller

NSFetchedResultsController section ordering using display order


I currently have an option that allows a user to change the display order of a category in my iPhone app.

I want to section the table view using a NSFetchedResultsController so that the section titles are the "category.name" ordered by "category.displayOrder" where "category" has a TO-ONE relationship with the entity I am fetching. The only way I can get the sectioning to work correctly is by using "category.displayOrder" as the section title.

NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:@"category.displayOrder" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];

NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, sortDescriptor2, nil];

[fetchRequest setSortDescriptors:sortDescriptors];

[fetchRequest setFetchBatchSize:10];

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                                                                                    managedObjectContext:managedObjectContext
                                                                                                      sectionNameKeyPath:@"category.name"
                                                                                                               cacheName:nil];

Any ideas on how I can name the section title something different then the property I am sorting with?


Solution

  • Not sure if I understand your question completely, but I do something similar in my app and here's how I get it to work:

    Firstly, the fetchedResultsController method, where I set the sort descriptions and predicates based on what I am trying to do. In this case I want to sort movie titles by release date THEN by name. Then with my predicate I grab entities of a specific 'type' and within a certain 'releaseDate' range.

    In my fetchresultscontroller definition, you set the sectionNameKeyPath to "releaseDate" so my section headers will be based on a date.

    - (NSFetchedResultsController *)fetchedResultsController {
    
        if (fetchedResultsController_ != nil) {
            return fetchedResultsController_;
        }
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        NSSortDescriptor *sortByReleaseDate = [[NSSortDescriptor alloc] initWithKey:@"releaseDate" ascending:NO];
        NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:YES];
        NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortByReleaseDate,sortByName, nil];
        [fetchRequest setSortDescriptors:sortDescriptors];
        [sortDescriptors release];
        [sortByName release];
        [sortByReleaseDate release];            
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(type == 'Movies') AND (releaseDate <= %@) AND (releaseDate >= %@)", [NSDate date], [NSDate dateWithTimeIntervalSinceNow:kOneDayTimeInterval*-30]];
        [fetchRequest setPredicate:predicate];
    
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Movie" inManagedObjectContext:self.managedObjectContext];
            [fetchRequest setEntity:entity];
    
        NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"releaseDate" cacheName:nil];
    
        ...// Perform and return fetch here, error handling etc...
    
        return fetchedResultsController_;
    
    }    
    

    Then in my table view data source delegate methods, I return the actual title for each header after transforming my NSDate into NSString (remember you have to return NSString for a tableview header title.

    - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    
        NSString *rawDateStr = [[[self.fetchedResultsController sections] objectAtIndex:section] name];
        //convert default date string to NSDate...
        NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
        [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss ZZ"];
        NSDate *date = [formatter dateFromString:rawDateStr];
    
        //convert NSDate to format we want...
        [formatter setDateFormat:@"d MMMM yyyy"];
        NSString *formattedDateStr = [formatter stringFromDate:date];
        return formattedDateStr;
    
    }
    

    So if I wanted to change the way my data is being displayed to be organised by titleName for instance, I'd change my fetchedResultsController object to:

    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"titleName" cacheName:nil];
    

    And modify my tableview:titleForHeaderInSection: data source method to simply return the titleName (which is already a string):

     - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    
            return [[[self.fetchedResultsController sections] objectAtIndex:section] name];
    }
    

    I hope this helps you find a solution to your specific problem.

    Cheers, Rog