Индексирование результатов из NSFetchedResultsController - PullRequest
5 голосов
/ 16 мая 2011

У меня возникли некоторые проблемы со смешанным, индексированным поисковым набором результатов из контроллера NSFetchedResults.Я настроил его для хранения индексированного первого инициала AZ для сущности, а затем хочу, чтобы он отображал числовые первые инициалы (т. Е. #, Как будет делать UILocalizedIndexCollation).

Я уже написал код, который сохраняет атрибут firstInitial объекта Artist как NSString @ "#", если полное имя начинается с цифры, и мне кажется, что код наполовину работает в моемUITableViewController с настроенным дескриптором сортировки.Проблема в том, что он работает только до тех пор, пока я не закрою / не перезапущу приложение.

На этом этапе раздел # из полученных результатов отображается вверху.Он будет оставаться там до тех пор, пока я не принудительно заставлю изменение данных (добавить / удалить управляемый объект), а затем произведу поиск записи и очисту поиск (используя searchDisplayController).В этот момент вступит в силу переупорядочение раздела, а раздел # будет перемещен вниз ...

Я явно что-то упускаю / слишком долго смотрел на один и тот же код.Кроме того, есть гораздо более простой способ сделать это, которого я не знаю / не могу найти в Google!

Любая помощь будет принята!

Спасибо

Sean

Ниже приведен соответствующий код из моего UITableViewController.

- (void)viewDidLoad
{
    // ----------------------------------
    // Various other view set up things in here....
    // ...
    // ...
    // ----------------------------------

    NSError *error;
    if (![[self artistResultsController] performFetch:&error]) {
        // Update to handle the error appropriately.
        NSLog(@"Failed to fetch artists: %@, %@", error, [error userInfo]);
        exit(-1);  // Fail
    }

}
- (NSFetchedResultsController *)artistResultsController {

if (_artistResultsController != nil) {
    return _artistResultsController;
}

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription 
                               entityForName:@"Artist" inManagedObjectContext:_context];
[fetchRequest setEntity:entity];

NSSortDescriptor *initialSort = [[NSSortDescriptor alloc] 
                          initWithKey:@"firstInitial" 
                          ascending:YES 
                          comparator:^(id obj1, id obj2) {
                              // Various number conditions for comparison - if it's a # initial, then it's a number
                              if (![obj1 isEqualToString:@"#"] && [obj2 isEqualToString:@"#"]) return NSOrderedAscending;
                              else if ([obj1 isEqualToString:@"#"] && ![obj2 isEqualToString:@"#"]) return NSOrderedDescending;
                              if ([obj1 isEqualToString:@"#"] && [obj2 isEqualToString:@"#"]) return NSOrderedSame;
                              // Else it's a string - compare it by localized region
                              return [obj1 localizedCaseInsensitiveCompare:obj2];
                          }];

NSSortDescriptor *nameSort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];

[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:initialSort, nameSort, nil]];

[fetchRequest setFetchBatchSize:20];

NSFetchedResultsController *theFetchedResultsController = 
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest 
                                    managedObjectContext:_context 
                                      sectionNameKeyPath:@"firstInitial" 
                                               cacheName:nil];
self.artistResultsController = theFetchedResultsController;
_artistResultsController.delegate = self;

[nameSort release];
[initialSort release];
[fetchRequest release];
[_artistResultsController release];

return _artistResultsController;}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    if (tableView == self.searchDisplayController.searchResultsTableView) {
        return nil;
    } else {
        return [[[_artistResultsController sections] objectAtIndex:section] name];
    }
}

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
    if (tableView == self.searchDisplayController.searchResultsTableView) {
        return nil;
    } else {
        return [[NSArray arrayWithObject:UITableViewIndexSearch] arrayByAddingObjectsFromArray:
                [[UILocalizedIndexedCollation currentCollation] sectionIndexTitles]];

    }
}

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
    if (tableView == self.searchDisplayController.searchResultsTableView) {
        return 0;
    } else {
        if (title == UITableViewIndexSearch) {
            [tableView scrollRectToVisible:self.searchDisplayController.searchBar.frame animated:NO];
            return -1;
        } 
        else {
            for (int i = [[_artistResultsController sections] count] -1; i >=0; i--) {
                NSComparisonResult cr = 
                [title localizedCaseInsensitiveCompare:
                 [[[_artistResultsController sections] objectAtIndex:i] indexTitle]];
                if (cr == NSOrderedSame || cr == NSOrderedDescending) {
                    return i;
                }
            }
            return 0;
        }
    }
}

РЕДАКТИРОВАТЬ: забыл упомянуть - мой поисковый фильтр использует предикат на контроллере fetchedResults, поэтому это вызывает новый запрос на выборкувот так

- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
    NSFetchRequest *aRequest = [_artistResultsController fetchRequest];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name BEGINSWITH[cd] %@", searchText];
    // set predicate to the request 
    [aRequest setPredicate:predicate];
    // save changes
    NSError *error = nil;
    if (![_artistResultsController performFetch:&error]) {
        NSLog(@"Failed to filter artists: %@, %@", error, [error userInfo]);
        abort();
    }  
} 

1 Ответ

4 голосов
/ 23 мая 2011

В итоге я решил исправить это по-другому.

У SortDescriptors, похоже, есть проблемы с выполнением пользовательской сортировки, когда вы также используете CoreData с SQLite для внутреннего хранилища.Я попробовал несколько вещей;Категории NSString с новым методом сравнения, блоком сравнения, как указано выше, и многократным обновлением таблицы, чтобы попытаться выполнить обновление с критерием сортировки.

В конце концов, я не смог принудительно вызвать дескриптор сортировкисделать первоначальную сортировку, поэтому я изменил реализацию.Я установил атрибут firstInitial для художников, чьи имена начинались с цифр, как «zzzz».Это означает, что CoreData будет корректно сортировать это (последние цифры).

После этого я жестко закодировал свой метод titleForHeaderInSection, чтобы вернуть # для заголовка, если это уместно, как показано ниже:

if ([[[[_artistResultsController sections] objectAtIndex:section] indexTitle] isEqualToString:@"zzzz"]) return [NSString stringWithString:@"#"];
return [[[_artistResultsController sections] objectAtIndex:section] indexTitle];

По сути, это означает, что это сортировка чисел в группировку 'zzzz', которая должна быть последней, и я просто игнорирую этот заголовок и говорю, что заголовок вместо этого #.

Не уверен, что есть лучший способчтобы сделать это, но он сохраняет всю сортировку внутри CoreData, которая, вероятно, более эффективна / масштабируема в долгосрочной перспективе.

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