NSFetchedResultsController упорядочение раздела с использованием порядка отображения - PullRequest
10 голосов
/ 08 декабря 2010

В настоящее время у меня есть опция, которая позволяет пользователю изменять порядок отображения категории в моем приложении для iPhone.

Я хочу разделить представление таблицы с помощью NSFetchedResultsController, чтобы заголовки разделов были "категорией".name "упорядочено по" category.displayOrder ", где" category "имеет отношение TO-ONE к сущности, которую я выбираю.Единственный способ заставить секционирование работать правильно - использовать "category.displayOrder" в качестве заголовка раздела.

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

Любые идеи о том, как я могу назвать название раздела чем-то другим, чем свойство, которым я являюсьсортировка с?

Ответы [ 4 ]

15 голосов
/ 08 декабря 2010

Не уверен, что я полностью понимаю ваш вопрос, но я делаю что-то подобное в своем приложении, и вот как я заставляю его работать:

Во-первых, метод fetchedResultsController, где я устанавливаю описания сортировки и предикаты на основена то, что я пытаюсь сделать.В этом случае я хочу отсортировать названия фильмов по дате выпуска, ТО по названию.Затем с моим предикатом я беру сущности определенного «типа» и в пределах определенного диапазона «releaseDate».

В моем определении fetchresultscontroller вы устанавливаете sectionNameKeyPath на «releaseDate», поэтому мои заголовки разделов будут основываться на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_;

}    

Затем в моем табличном представлении методы делегата источника данных я возвращаю фактический заголовок для каждого заголовка после преобразования моего NSDate в NSString (помните, что вы должны вернуть NSString для заголовка заголовка таблицы.

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

}

Так что, если бы я хотел изменить способ отображения моих данных, например, по названию titleName, я бы изменил свой объект fetchedResultsController на:

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

И изменил бы свое табличное представление: titleForHeaderInSection: метод источника данных для простого возврата titleName (которое уже является строкой):

 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {

        return [[[self.fetchedResultsController sections] objectAtIndex:section] name];
}

Надеюсь, это поможет вам найти решение вашей конкретной проблемы.

Cheers, Rog

10 голосов
/ 29 мая 2012

У меня была та же проблема, и у меня есть другое решение - использовать временное свойство для sectionNameKeyPath, то есть category.sectionName. Похоже, что если вы используете свойство основных данных в качестве sectionNameKeyPath, оно попытается отсортировать данные.

Итак, используя ваш пример выше:

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

Тогда, по вашему мнению, контроллер:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
    id<NSFetchedResultsSectionInfo> sectionInfo=[self.resultsController.sections objectAtIndex:section];
    return sectionInfo.name;
}
3 голосов
/ 09 декабря 2010

Благодаря Рогу я смог обернуть голову вокруг этой проблемы и найти решение.Это не так чисто, как хотелось бы, поэтому, если у кого-то есть лучший подход, я хотел бы услышать об этом.

Исходя из того, что я узнал (я могу ошибаться, пожалуйста, сообщите мне), вы можетеНазовите разделы (sectionNameKeyPath) по свойству, в котором вы заказываете свою таблицу.В моем случае я хотел отсортировать свою таблицу, используя свойство displayOrder, но я не хотел, чтобы заголовки разделов были 0, 1, 2 и т. Д. Вместо этого я хотел, чтобы заголовки разделов использовали свойство title «Name1»,«Имя2» и т. Д., Которое соответствует displayOrder.Для этого я определяю заголовок заголовка на основе displayOrder в titleForHeaderInSection:

        NSString *displayOrder = [sectionInfo name];

        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        [fetchRequest setEntity:[NSEntityDescription entityForName:@"Platform" inManagedObjectContext:managedObjectContext]];

        NSNumberFormatter * numberFormatter = [[NSNumberFormatter alloc] init];
        [numberFormatter setNumberStyle:NSNumberFormatterNoStyle];

        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"displayOrder == %@", [numberFormatter numberFromString:displayOrder]];
        [fetchRequest setPredicate:predicate];

        Platform *platform;
        NSError *error = nil;
        NSArray *fetchResults = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
        if ([fetchResults count] > 0) {
            platform = [fetchResults objectAtIndex:0];
            headerView.titleLabel.text = [platform name];
        } else {
            headerView.titleLabel.text = @"Unknown";
        }

        [numberFormatter release];
        [fetchRequest release];

Спасибо, Рог!

0 голосов
/ 28 августа 2015

В моем случае мне нужно было RTFM:

- initWithFetchRequest:managedObjectContext:sectionNameKeyPath:cacheName:

Если это sectionNameKeyPath не совпадает с указанным в дескрипторе первой сортировки в fetchRequest, они должны сгенерировать таких же относительных порядков . Например, первый дескриптор сортировки в fetchRequest может указывать ключ для постоянного свойства; sectionNameKeyPath может указывать ключ для временного свойства, полученного из постоянного свойства.

Что создавало несоответствия в порядке табличного представления, когда свойство sectionNameKeyPath или NSSortDescriptor управляемого объекта изменилось на порядок сортировки, отличный от другого свойства.

Источник:
https://richardwarrender.com/2010/10/core-data-objects-in-wrong-sections/

...