NSSortDescriptor Problem

I am creating a contact application in which I get the names from the AddressBook and save them in the master data and display the names in the table using NSFetchedResultsController.However , the first index and section that appear are followed by alphabets. But I want to do this, as in the application for native contacts. Ie # Index should come at last.
I used the following NSortDescriptor :

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

here "fullName" is the key in the master data, which is created by combining the first and last name. And the section identifier is the first letter of "fullName", if fullName does not start with the alphabet, its section identifier is #.
I searched about this and used NSDiacriticInsensitiveSearch in the NSortDescriptor comparator, but this did not work. If anyone has an idea, let me know.

Here is 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 begins with a non-alphabet, and then returns the alphabet with which it begins.

newContact - object of the object.

+4
source share
4 answers

In the object, you can save an additional attribute sortName , which is fullName if the name begins with a letter, and <C>fullName otherwise. <C> is a fixed character that is "larger" than all letters. for instance

 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 will be "#" if sortName starts with a special character.

The disadvantage is that you need to keep an additional attribute, the advantage is that you can continue to use the selected result controller (which can only use persistent attributes for sorting).

UPDATE: Actually, this can be done a little easier.

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

 NSString *special = @"\uE000"; if ([[NSCharacterSet letterCharacterSet] characterIsMember:[contact.contactName characterAtIndex:0]]) { contact.sectionIdentifier = [contact.contactName substringToIndex:1]; } else { contact.sectionIdentifier = special; } 

The result controller uses sectionIdentifier to group and sort sections. Entries in 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 title # 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; } 
+6
source

You can split the results into two arrays, one of which begins with alpha characters, and the other does not. Then just add them together. Assuming you start with an array of managed objects called results :

 //Create the sort descriptor array NSSortDescriptor *sd = [NSSortDescriptor sortDescriptorWithKey:@"fullName" ascending:YES]; NSArray *descriptors = [NSArray arrayWithObject:sd]; //Create a sorted array of objects where fullName starts with an alpha character //using a Regex NSPredicate *pred = [NSPredicate predicateWithFormat:@"fullName MATCHES '^[a-zA-Z].*'"]; NSArray *alpha = [[results filteredArrayUsingPredicate:pred] sortedArrayUsingDescriptors:descriptors]; //Now use the alpha array to create an array of objects where the fullName does not //start with an alpha character NSMutableArray *nonAlpha = [results mutableCopy]; [nonAlpha removeObjectsInArray:alpha]; [nonAlpha sortUsingDescriptors:descriptors]; //Now put them back together again NSArray *sortedResults = [alpha arrayByAddingObjectsFromArray:nonAlpha]; //And if you're not using ARC! [nonAlpha release]; 
+2
source

You can do it:

 sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"fullName" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)]; 

Just make sure that this does not affect performance in your case.

+1
source

You can try writing your own comparator function

Assuming it sorts ManagedObjects, and they all have fullName as a field, the following may help.

 [[NSSortDescriptor alloc] initWithKey:@"FullName" ascending:YES comparator:^NSComparisonResult(id obj1, id obj2) { return [[obj1 objectForKey:@"fullName"] compare:[obj2 objectForKey:@"fullName"] options:NSCaseInsensitiveSearch]; }]; 

The advantage of this is that you can also write an NSLog for each comparison and see what happens.

0
source

Source: https://habr.com/ru/post/1438648/


All Articles