Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 901
avenged Avatar asked Dec 08 '10 06:12

avenged


2 Answers

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

like image 154
Rog Avatar answered Oct 27 '22 16:10

Rog


I had the same problem and I've another solution - use a transient property for the sectionNameKeyPath i.e. category.sectionName. It seems that if you use a core data property as a sectionNameKeyPath it will try and sort with that.

So using your example above:

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

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

Category.h:

@interface Category : NSManagedObject

@property (nonatomic, retain) NSString* name;
@property (nonatomic, retain) NSNumber* displayOrder;

-(NSString*) sectionName;

@end

Category.m:

@implementation Category

@dynamic name;
@dynamic displayOrder;

-(NSString*) sectionName{
    return self.name;
}

@end

Then in your view controller:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
    id<NSFetchedResultsSectionInfo> sectionInfo=[self.resultsController.sections objectAtIndex:section];
    return sectionInfo.name;
}
like image 28
Mark Horgan Avatar answered Oct 27 '22 16:10

Mark Horgan