Nsfetchedresultscontroller V.S. Uilocalizedindexedcollation

NSFetchedResultsController with indexed UITableViewController and UILocalizedIndexedCollation

I think you have to traverse the fetched results controller sections and find a matching section for the given title, for example:

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
NSInteger section = 0;
for (id <NSFetchedResultsSectionInfo> sectionInfo in [_fetchedResultsController sections]) {
if ([sectionInfo.indexTitle compare:title] >= 0)
break;
section++;
}
return section;
}

For section index titles that to not have a matching section, you have to decide if you want to jump to a "lower" or "higher" section. The above method jumps to the next higher section.

How to use the first character as a section name

You should just pass "name" as the sectionNameKeyPath. See this answer to the question "Core Data backed UITableView with indexing".

UPDATE

That solution only works if you only care about having the fast index title scroller. In that case, you would NOT display the section headers. See below for sample code.

Otherwise, I agree with refulgentis that a transient property is the best solution. Also, when creating the NSFetchedResultsController, the sectionNameKeyPath has this limitation:

If this key path is not the same as
that specified by the first sort
descriptor in fetchRequest, they must
generate the same relative orderings.
For example, the first sort descriptor
in fetchRequest might specify the key
for a persistent property;
sectionNameKeyPath might specify a key
for a transient property derived from
the persistent property.

Boilerplate UITableViewDataSource implementations using 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];
//}

UPDATE 2

For the new 'uppercaseFirstLetterOfName' transient property, add a new string attribute to the applicable entity in the model and check the "transient" box.

There are a few ways to implement the getter. If you are generating/creating subclasses, then you can add it in the subclass's implementation (.m) file.

Otherwise, you can create a category on NSManagedObject (I put this right at the top of my view controller's implementation file, but you can split it between a proper header and implementation file of its own):

@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

Also, in this version, don't forget to pass 'uppercaseFirstLetterOfName' as the sectionNameKeyPath:

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

And, to uncomment tableView:titleForHeaderInSection: in the UITableViewDataSource implementation:

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

UILocalizedIndexedCollation with custom sections

After some research, it turns out that i can implement my own version uilocalizedindexedcollation that does the things i want.

i just need to have these basic properties and functions and i can make it do just about anything i want.

// Provides the list of section titles used to group results (e.g. A-Z,# in US/English)
@property(nonatomic, readonly) NSArray *sectionTitles;

// Provides the list of index titles used to quickly jump to particular sections
@property(nonatomic, readonly) NSArray *sectionIndexTitles;

// Specifies the section that should be scrolled to for the title at the given index.
// This method allows you to map between a given item in the index
// and a given section where there isn't a one-to-one mapping.
- (NSInteger)sectionForSectionIndexTitleAtIndex:(NSInteger)indexTitleIndex;

// Returns the index of the section that will contain the object.
// selector must not take any arguments and return an NSString.
- (NSInteger)sectionForObject:(id)object collationStringSelector:(SEL)selector;

// Used for sorting objects within the same section.
// selector must not take any arguments and return an NSString.
// In the process of sorting the array, each object may receive
// selector multiple times, so this method should be fast.
- (NSArray *)sortedArrayFromArray:(NSArray *)array collationStringSelector:(SEL)selector;

how does UILocalizedIndexedCollation work? (sample code from documentation)

[collation sectionTitles] simply contains the standard alphabet sorting for display along the right-hand side of the tableview. Even if you only have items starting with 'B' and 'F', the index on the side always shows the full alphabet. But since the standard alphabet used for sorting strings changes depending on the locale, Apple gave us UILocalizedIndexedCollation to make it easy to show the user what they'd expect.

CoreData: NSSortDescriptor - Á to A, à to A (@selector)

You should use UILocalizedIndexedCollation for doing sorting and categorizing entries into sections. The code for implementing this is in the question NSFetchedResultsController v.s. UILocalizedIndexedCollation

The UILocalizedIndexedCollation was built to be able to on a per-language basis categorize words based on the current language settings. Á and à will be put in section A.

NSFetchedResultsController custom sort not getting called

There are a couple of things I can think of. First, though this may not be your problem, you cannot sort on transient properties. But more likely is that when sorting in a model backed by a SQL store, the comparator gets "compiled" to a SQL query, and not all Objective-C functions are available. In this case, you'd need to sort in memory after the fetch is performed.

EDIT: See this doc, specifically the Fetch Predicates and Sort Descriptors section.



Related Topics



Leave a reply



Submit