Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSSortDescriptor issue

I am making a contact book App where I am fetching names from AddressBook and stored them in Core data and displayed the names on a table using NSFetchedResultsController.However the first index and section that comes up is # followed by the alphabets. But I want to do it like it is in native contact app i.e. # index should come at last.
I used the following NSortDescriptor:

sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"fullName" ascending:YES ];

here "fullName" is the key in core data which is made by concatenating first name and last name. And the section identifier is the first letter of "fullName" if the fullName doesn't start with alphabet, its section identifier is #.
I had searched about it and used NSDiacriticInsensitiveSearch in the NSortDescriptor comparator but it didn't worked. If any one has any idea then let me know.

Here goes my code:

NSString *special = @"\uE000";
if ([[self sectionName:contactName] isEqualToString:@"#"]) {                           
    sortName = [special stringByAppendingString:contactName];
}
else{
    sortName = contactName;
}
[newContact setValue:[self sectionIdentifier:sortName] forKey:@"sectionIdentifier"];
[newContact setValue:sortName forKey:@"sortName"];

And here is the sort descriptor:

sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"sortName" ascending:YES];

[self sectionIdentifier:sortName] this method returns # if sortName starts with a non alphabet and else it returns the alphabet by which it starts.

newContact is the object of the entity.

like image 229
user1632153 Avatar asked Jan 15 '23 09:01

user1632153


1 Answers

You could store an additional attribute sortName in the entity, which is fullName if the name starts with a letter, and <C>fullName otherwise. <C> is a fixed character which is "greater" than all letters. For example

NSString *special = @"\uE000";
if ("fullName starts with letter")
    sortName = fullName;
else
    sortName = [special stringByAppendingString:fullName];

Now you can sort according to sortName, and the section identifier would be "#" if sortName starts with the special character.

The disadvantage is that you have to store an additional attribute, the advantage is that you can continue to use a fetched results controller (which can use only persistent attributes for sorting).

UPDATE: It can actually be done a bit easier.

When you create a new entry, you set sectionIdentifier to the first character of the name if it is a letter, and to the special character otherwise:

NSString *special = @"\uE000";

if ([[NSCharacterSet letterCharacterSet] characterIsMember:[contact.contactName characterAtIndex:0]]) {
    contact.sectionIdentifier = [contact.contactName substringToIndex:1];
} else {
    contact.sectionIdentifier = special;
}

The fetched results controller uses sectionIdentifier for grouping and sorting the sections. Entries within each section are sorted by contactName:

NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Contact"];
NSSortDescriptor *sort1 = [NSSortDescriptor sortDescriptorWithKey:@"sectionIdentifier"
             ascending:YES selector:@selector(localizedStandardCompare:)];
NSSortDescriptor *sort2 = [NSSortDescriptor sortDescriptorWithKey:@"contactName"
             ascending:YES selector:@selector(localizedStandardCompare:)];
[request setSortDescriptors:@[sort1, sort2]];
self.frc = [[NSFetchedResultsController alloc] initWithFetchRequest:request
                   managedObjectContext:self.context
                     sectionNameKeyPath:@"sectionIdentifier"
                              cacheName:nil];

All non-letter entries are now grouped in the last section. The final step is to display the correct section header # for the last section:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.frc sections] objectAtIndex:section];
    NSString *title = [sectionInfo name];
    if ([title isEqualToString:special])
        title = @"#";
    return title;
}
like image 143
Martin R Avatar answered Jan 25 '23 07:01

Martin R