Как использовать первый символ в качестве названия раздела - PullRequest
38 голосов
/ 16 ноября 2009

Я использую Базовые данные для представления таблицы, и я хотел бы использовать первую букву каждого из моих результатов в качестве заголовка раздела (чтобы я мог получить индекс раздела на стороне). Есть ли способ сделать это с помощью ключевого пути? Примерно ниже, где я использую name.firstLetter в качестве sectionNameKeyPath (к сожалению, это не работает).

Нужно ли мне брать первую букву каждого результата вручную и создавать мои разделы подобным образом? Лучше ли ввести новое свойство, чтобы просто держать первую букву и использовать его как sectionNameKeyPath?

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

Спасибо.

** РЕДАКТИРОВАТЬ: ** Я не уверен, если это имеет значение, но мои результаты японские, отсортированные по Katakana. Я хочу использовать эти катаканы в качестве индекса секции.

Ответы [ 6 ]

79 голосов
/ 16 ноября 2009

Вы должны просто передать "name" как sectionNameKeyPath. См. ответ на вопрос «Базовые данные, поддерживаемые UITableView с индексацией».

UPDATE
Это решение работает только в том случае, если вам нужна только быстрая прокрутка заголовка индекса. В этом случае вы бы НЕ отображали заголовки разделов. Ниже приведен пример кода.

В противном случае, я согласен с refulgentis, что переходное свойство является лучшим решением. Кроме того, при создании NSFetchedResultsController, sectionNameKeyPath имеет следующее ограничение:

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

Реализации UITableViewDataSource в Boilerplate с использованием NSFetchedResultsController:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return [[fetchedResultsController sections] count];
}

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    return [fetchedResultsController sectionIndexTitles];
}

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
    return [fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects];
}

// Don't implement this since each "name" is its own section:
//- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
//    id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
//    return [sectionInfo name];
//}

ОБНОВЛЕНИЕ 2

Для нового переходного свойства uppercaseFirstLetterOfName добавьте новый строковый атрибут к соответствующему объекту в модели и установите флажок «переходный».

Есть несколько способов реализации геттера. Если вы генерируете / создаете подклассы, вы можете добавить его в файл реализации (.m) подкласса.

В противном случае вы можете создать категорию в NSManagedObject (я поместил это прямо в начало файла реализации моего контроллера представления, но вы можете разделить его между соответствующим заголовком и собственным файлом реализации):

@interface NSManagedObject (FirstLetter)
- (NSString *)uppercaseFirstLetterOfName;
@end

@implementation NSManagedObject (FirstLetter)
- (NSString *)uppercaseFirstLetterOfName {
    [self willAccessValueForKey:@"uppercaseFirstLetterOfName"];
    NSString *aString = [[self valueForKey:@"name"] uppercaseString];

    // support UTF-16:
    NSString *stringToReturn = [aString substringWithRange:[aString rangeOfComposedCharacterSequenceAtIndex:0]];

    // OR no UTF-16 support:
    //NSString *stringToReturn = [aString substringToIndex:1];

    [self didAccessValueForKey:@"uppercaseFirstLetterOfName"];
    return stringToReturn;
}
@end

Также, в этой версии не забудьте передать 'uppercaseFirstLetterOfName' в качестве sectionNameKeyPath:

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext
sectionNameKeyPath:@"uppercaseFirstLetterOfName" // this key defines the sections
cacheName:@"Root"];

И, чтобы раскомментировать tableView:titleForHeaderInSection: в реализации UITableViewDataSource:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
    return [sectionInfo name];
}
11 голосов
/ 16 ноября 2009

Возможно, есть более элегантный способ сделать это, но у меня недавно была та же проблема, и я нашел это решение.

Сначала я определил переходное свойство для индексируемых объектов с именем firstLetterOfName и записал метод получения в файл .m для объекта. e.x.

- (NSString *)uppercaseFirstLetterOfName {
    [self willAccessValueForKey:@"uppercaseFirstLetterOfName"];
    NSString *stringToReturn = [[self.name uppercaseString] substringToIndex:1];
    [self didAccessValueForKey:@"uppercaseFirstLetterOfName"];
    return stringToReturn;
}

Далее я настроил свой запрос на выборку / сущности для использования этого свойства.

NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Object" inManagedObjectContext:dataContext];
[request setEntity:entity];
[NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(caseInsensitiveCompare:)];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];

Дополнительное замечание, по поводу ничего: будьте осторожны с NSFetchedResultsController - он еще не совсем выпечен, ИМХО, и в любой ситуации, кроме простых случаев, перечисленных в документации, вам, вероятно, будет лучше сделать это «старомодным» способом.

4 голосов
/ 10 августа 2011

Я решил это с помощью UILocalizedIndexCollation, как указано в NSFetchedResultsController v.s. UILocalizedIndexedCollation вопрос

1 голос
/ 08 ноября 2012

См. Мой ответ на аналогичный вопрос здесь , в котором я описываю, как создавать локализованные постоянные индексы sectionKey (поскольку вы не можете сортировать по переходным атрибутам в NSFetchedResultsController).

1 голос
/ 20 февраля 2012

Элегантный способ - сделать свойство firstLetter временным, ОДНАКО на практике медленным.

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

Быстрый, но не элегантный способ - создать непереходное свойство "firstLetter", которое вы обновляете каждый раз, когда устанавливаете свойство "name". Несколько способов сделать это: переопределить "setName:" оценщик, переопределить "willSave", KVO.

0 голосов
/ 24 февраля 2018

Вот простое решение для obj-c, с опозданием на несколько лет и одним языком. Это работает и работает быстро в моем проекте.

Сначала я создал категорию на NSString, назвав файл NSString + Indexing. Я написал метод, который возвращает первую букву строки

@implementation NSString (Indexing)

- (NSString *)stringGroupByFirstInitial {
    if (!self.length || self.length == 1)
        return self;
    return [self substringToIndex:1];
}

Затем я использовал этот метод в определении контроллера извлеченных результатов следующим образом

_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"name.stringGroupByFirstInitial"
                                                                               cacheName:nil];

Приведенный выше код в сочетании с двумя методами stringIndex будет работать без необходимости возиться с переходными свойствами или сохранять отдельный атрибут, который просто хранит первую букву в базовых данных

Однако, когда я пытаюсь сделать то же самое со Swift, выдается исключение, так как ему не нравится иметь функцию в качестве пути ключа детали (или вычисляемое свойство строки - я тоже это пытался) то же самое в Swift я бы очень хотел узнать как.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...