Столкнувшись с той же проблемой в последнее время, я вынужден искать в сети (прежде всего, в стеке) поток подходящего решения, чтобы заставить NSFetchedResultsController (FRC) и UILocalizedIndexedCollation (LIC) работать вместе. Большинство найти решения не было достаточно хорошо, чтобы выполнить все требования. Важно отметить, что мы не можем использовать LIC для сортировки извлеченных объектов так, как это нужно, конечно, у нас будет огромная потеря производительности, а FRC не даст использовать все преимущества.
Итак, проблема в общем:
1) У нас есть БД с какими-то данными, которые мы хотим извлечь и отобразить с помощью FRC в списке (UITableView) с индексами (аналогично Contacts.app). Нам нужно передать ключ значения объекта, чтобы FRC мог принять решение о сортировке.
2) Даже если мы добавим специальное поле в наши модели CoreData для сортировки разделов и используем заголовки разделов FRC, мы не добьемся желаемого результата, курс FRC дает только найденные индексы, но не полный алфавит. Вдобавок к этому мы столкнемся с проблемой некорректного отображения индексов (не совсем уверен, почему так, может быть, какая-то ошибка в FRC). Например, в случае русского алфавита будут совершенно пустые или «странные» символы ($,?, ',…).
3) Если мы попытаемся использовать LIC для отображения хороших локализованных индексов, мы столкнемся с проблемой отображения разделов на основе данных в FRC для завершения локализованных «разделов» алфавита в LIC.
4) После того, как мы решили использовать LIC и каким-то образом решить проблему 3) мы заметим, что LIC поместит раздел «#» снизу (т. Е. Самый высокий индекс раздела), а FRC поместит «#» -подобные объекты наверх (т.е. самый низкий индекс секции - 0). Так будет иметь полное смещение разделов.
С учетом всего этого я решил «обмануть» FRC без какого-либо большого «взлома», но заставить его сортировать данные так, как мне нужно (переместить все объекты из раздела «#», например, в конец списка) .
Вот решение, к которому я пришел:
Я добавляю метод расширения к своему экземпляру NSManagedObject, чтобы подготовить имя сортировки, которое мы будем использовать в дескрипторе сортировки и пути ключа раздела для настройки FRC. Никаких специальных действий не требуется, кроме тех, которые будут описаны ниже.
Проблема 4) возникает из-за алгоритмов сортировки FRC (низкоуровневый SQL), которые могут быть слегка изменены: только путем применения дескрипторов сортировки, которые больше зависят от ваших данных, предикатов и использования фиксированных предопределенных компараторов, которые не решают проблема.
Я заметил, что FRC решает, что символ "#" ниже, чем любой символ алфавита, противоположный LIC, где "#" самый высокий.
Логика FRC довольно проста, потому что символ "#" в UTF-8 - это U + 0023. И латинская заглавная буква «A» - это U + 0041, поэтому 23 <41. Чтобы FRC поместил «#» -подобный объект в секцию с наивысшим индексом, нам нужно передать наибольший символ UTF-8. Для этого источника <a href="http://www.utf8-chartable.de/unicode-utf8-table.pl" rel="nofollow">http://www.utf8 -chartable.de / unicode-utf8-table.pl этот символ UTF-8 имеет значение U + 1000FF (?). Конечно, в реальной жизни этот символ практически не встречается. Давайте используем U + 100000 для ясности.
Метод обновления имени сортировки выглядит примерно так:
#define UT8_MAX @"\U00100000"
- (void)updateSortName
{
NSMutableString *prSortName = [NSMutableString stringWithString:[self dataDependantSortName]]; // for sort descriptors
NSString *prSectionIdentifier = [[prSortName substringToIndex:1] uppercaseString]; // section keypath
UILocalizedIndexedCollation *collation = [UILocalizedIndexedCollation currentCollation];
NSUInteger sectionIndex = [collation sectionForObject:prSectionIdentifier collationStringSelector:@selector(stringValue)]; // stringValue is NSString category method that returns [NSString stringWithString:self]
if(sectionIndex == [[collation sectionTitles] count] - 1) // last section tile '#'
{
prSectionIdentifier = UT8_MAX;
}
else
{
prSectionIdentifier = [collation sectionTitles][sectionIndex];
}
[prSortName replaceCharactersInRange:NSMakeRange(0, 1) withString:prSectionIdentifier];
// sortName, sectionIdentifier - non-transient string attributes in CoreData model
[self willChangeValueForKey:@"sortName"];
[self setPrimitiveValue:prSortName forKey:@"sortName"];
[self didChangeValueForKey:@"sortName"];
[self willChangeValueForKey:@"sectionIdentifier"];
[self setPrimitiveValue:prSectionIdentifier forKey:@"sectionIdentifier"];
[self didChangeValueForKey:@"sectionIdentifier"];
}
Настройка FRC:
- (void)setupFRC
{
NSEntityDescription *entityDescription =
[NSEntityDescription entityForName:@"entity"
inManagedObjectContext:self.moc];
NSSortDescriptor *sortNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"sortName" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)]; // or any selector you need
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortNameDescriptor, nil];
NSFetchRequest *fetchRequest = [NSFetchRequest new];
[fetchRequest setEntity:entityDescription];
[fetchRequest setFetchBatchSize:BATCH_SIZE];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *fetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.moc
sectionNameKeyPath:@"sectionIdentifier"
cacheName:nil];
self.fetchedResultsController = fetchedResultsController;
}
Методы делегата FRC по умолчанию. Методы делегата и источника данных:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return [[self localizedIndexedCollation] sectionTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
NSString *indexTitle = [title isEqualToString:@"#"] ? UT8_MAX : title;
NSInteger fetchTitleIndex = NSNotFound;
NSArray *sections = [self.fetchedResultsController sections];
for (id <NSFetchedResultsSectionInfo> sectionInfo in sections)
{
if([[sectionInfo name] isEqualToString:indexTitle])
{
fetchTitleIndex = [sections indexOfObject:sectionInfo];
break;
}
}
return fetchTitleIndex;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
NSString *fetchTitle = [sectionInfo name];
NSInteger collationTitleIndex = [[self localizedIndexedCollation] sectionForObject:fetchTitle
collationStringSelector:@selector(stringValue)];
return [[[self localizedIndexedCollation] sectionTitles] objectAtIndex:collationTitleIndex];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
Вот и все. Пока работает хорошо. Может быть, это будет работать для вас.