Note that there are some explanatory texts on larger screens.

plurals
  1. POIndexing results from an NSFetchedResultsController
    primarykey
    data
    text
    <p>I'm having some issues with a mixed, indexed, searchable results set from an NSFetchedResults controller. I have it set up to store an indexed A-Z first initial for an entity, and then want it to display numeric first initials (i.e. # as the UILocalizedIndexCollation would do). </p> <p>I have already written the code that saves a "firstInitial" attribute of an Artist object as NSString @"#" if the full name started with a number, and I seem to have gotten the code half working in my UITableViewController with a customised sort descriptor. The problem is that it only works until I quit/relaunch the app. </p> <p>At this point, the # section from the fetched results appears at the top. It will stay there until I force a data change (add/remove a managed object) and then search for an entry, and clear the search (using a searchDisplayController). At this point the section re-ordering will kick in and the # section will be moved to the bottom...</p> <p>I'm obviously missing something/have been staring at the same code for too long. Alternatively, there's a much easier way of doing it which I'm not aware of/can't find on Google!</p> <p>Any help would be appreciated!</p> <p>Thanks</p> <p>Sean</p> <p>The relevant code from my UITableViewController is below.</p> <pre><code>- (void)viewDidLoad { // ---------------------------------- // Various other view set up things in here.... // ... // ... // ---------------------------------- NSError *error; if (![[self artistResultsController] performFetch:&amp;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:@"#"] &amp;&amp; [obj2 isEqualToString:@"#"]) return NSOrderedAscending; else if ([obj1 isEqualToString:@"#"] &amp;&amp; ![obj2 isEqualToString:@"#"]) return NSOrderedDescending; if ([obj1 isEqualToString:@"#"] &amp;&amp; [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 &gt;=0; i--) { NSComparisonResult cr = [title localizedCaseInsensitiveCompare: [[[_artistResultsController sections] objectAtIndex:i] indexTitle]]; if (cr == NSOrderedSame || cr == NSOrderedDescending) { return i; } } return 0; } } } </code></pre> <p>EDIT: Forgot to mention - my search filter is using a predicate on the fetchedResults controller, so this causes a new fetch request, like so</p> <pre><code>- (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:&amp;error]) { NSLog(@"Failed to filter artists: %@, %@", error, [error userInfo]); abort(); } } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload