1

I want to populate a UITableView with help from a NSFetchedResultsController. I want the table view to have sections for the first letter in the title of the entity. The table view should also have an index list on the right.

This is what my entity looks like:

@interface Book : NSManagedObject

@property (nonatomic, copy) NSString *title;

@end

My first plan was to have a transient property that returned the first letter in the title:

- (NSString *)firstLetter
{
    // Edge-cases checking code not included
    return [self.title substringToIndex:1];
}

The problem is that it will give you different sections for '2' and '3' for instance, I want all numbers and symbols in one section called '#', just like what the UILocalizedIndexCollation gives you.

But which letters should be in the '#' category? Of course it depends on the language, in English 'Ö' should be in the other category, but in Swedish it should be in its own category. So I thought that maybe I should just use NSLocalizedIndexCollation instead. Here is that code:

- (void)recalculateSectionizedBooks
{
    SEL selector = @selector(title);
    NSInteger sectionTitlesCount = [[[UILocalizedIndexedCollation currentCollation] sectionTitles] count];

    NSMutableArray *mutableSections = [NSMutableArray arrayWithCapacity:sectionTitlesCount];
    for (NSUInteger idx = 0; idx < sectionTitlesCount; idx++)
    {
        [mutableSections addObject:[NSMutableArray array]];
    }

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Book"];
    NSError *error = nil;
    NSArray *result = [self.context executeFetchRequest:request
                                                  error:&error];
    if (error)
    {
        NSLog(@"Error fetching: %@", error);
    }

    for (Book *book in result)
    {
        NSInteger sectionNumber = [[UILocalizedIndexedCollation currentCollation] sectionForObject:book
                                                                           collationStringSelector:selector];
        [mutableSections[sectionNumber] addObject:book];
    }

    for (NSUInteger idx = 0; idx < sectionTitlesCount; idx++)
    {
        NSArray *objectsForSection = mutableSections[idx];
        mutableSections[idx] = [[UILocalizedIndexedCollation currentCollation] sortedArrayFromArray:objectsForSection
                                                                            collationStringSelector:selector];
    }

    self.sectionizedBooks = mutableSections;
}

It is called in -viewWillAppear:, after that I reload my table view. The data source method are correctly set up, and also makes sure there are no empty sections.

This solution has two problems. The first one is that it requires a huge amount of code (much more than the snippet I showed you) to work. The second, more serious, problem is that the performance is very bad. Every time the view apperas, the sections has to be recalculated because something affecting it might have changed. It took 30 seconds with 10k books which is unacceptable.

I think I need a solution that combines NSFetchedResultsController and UILocalizedCollation in a clever way. Here's what I need from the solution:

  • Having the books in sections based on the first letter in the title
  • An "other" category ('#') should exist in the end
  • No empty sections
  • Index list on the right with all letters possible + '#' in the end
  • High performance, at least after the first time the view appears
user3956212
  • 359
  • 2
  • 14

0 Answers0