Cómo usar el primer carácter como un nombre de sección

Estoy usando Core Data para una vista de tabla, y me gustaría usar la primera letra de cada uno de mis resultados como encabezado de sección (para que pueda obtener el índice de sección en el lateral). ¿Hay alguna manera de hacer esto con la ruta clave? Algo como abajo, donde uso name.firstLetter como sectionNameKeyPath (desafortunadamente eso no funciona).

¿Tengo que tomar la primera letra de cada resultado manualmente y crear mis secciones de esa manera? ¿Es mejor sectionNameKeyPath una nueva propiedad para guardar la primera letra y usarla como sectionNameKeyPath ?

 NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:@"name.firstLetter" cacheName:@"Root"]; 

Gracias.

** EDITAR: ** No estoy seguro si hace una diferencia, pero mis resultados son japoneses, ordenados por Katakana. Quiero usar estos Katakana como el índice de sección.

Debería simplemente pasar “nombre” como la sección Nombre clave clave. Vea esta respuesta a la pregunta “Core Data respaldado UITableView con indexación”.

ACTUALIZAR
Esa solución solo funciona si solo te importa tener el desplazamiento rápido del título del índice. En ese caso, NO mostraría los encabezados de las secciones. Vea a continuación el código de muestra.

De lo contrario, estoy de acuerdo con refulgentis en que una propiedad transitoria es la mejor solución. Además, al crear NSFetchedResultsController, sectionNameKeyPath tiene esta limitación:

Si esta ruta clave no es la misma que la especificada por el primer descriptor de clasificación en fetchRequest, deben generar los mismos pedidos relativos. Por ejemplo, el primer descriptor de clasificación en fetchRequest podría especificar la clave para una propiedad persistente; sectionNameKeyKey podría especificar una clave para una propiedad transitoria derivada de la propiedad persistente.

Completas implementaciones UITableViewDataSource utilizando 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  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  sectionInfo = [[fetchedResultsController sections] objectAtIndex:section]; // return [sectionInfo name]; //} 

ACTUALIZACIÓN 2

Para la nueva propiedad transitoria ‘uppercaseFirstLetterOfName’, agregue un nuevo atributo de cadena a la entidad aplicable en el modelo y marque la casilla “transitoria”.

Hay algunas formas de implementar el getter. Si está generando / creando subclases, puede agregarlo en el archivo de implementación de la subclase (.m).

De lo contrario, puede crear una categoría en NSManagedObject (lo puse en la parte superior del archivo de implementación de mi controlador de vista, pero puede dividirlo entre un encabezado adecuado y un archivo de implementación propio):

 @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 

Además, en esta versión, no se olvide de pasar ‘uppercaseFirstLetterOfName’ como la secciónNameKeyKey:

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

Y, para descomentar tableView:titleForHeaderInSection: en la implementación de UITableViewDataSource:

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

Puede haber una manera más elegante de hacer esto, pero recientemente tuve el mismo problema y se me ocurrió esta solución.

Primero, definí una propiedad transitoria en los objetos que estaba indexando llamada firstLetterOfName, y escribí el getter en el archivo .m para el objeto. ex

 - (NSString *)uppercaseFirstLetterOfName { [self willAccessValueForKey:@"uppercaseFirstLetterOfName"]; NSString *stringToReturn = [[self.name uppercaseString] substringToIndex:1]; [self didAccessValueForKey:@"uppercaseFirstLetterOfName"]; return stringToReturn; } 

A continuación, configuré mi solicitud de búsqueda / entidades para usar esta propiedad.

 NSFetchRequest *request = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"Object" inManagedObjectContext:dataContext]; [request setEntity:entity]; [NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(caseInsensitiveCompare:)]; NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor]; [request setSortDescriptors:sortDescriptors]; 

Nota al margen, a propósito de nada: tenga cuidado con NSFetchedResultsController – aún no está completamente horneado, en mi humilde opinión, y en cualquier situación más allá de los casos simples enumerados en la documentación, probablemente será mejor que lo haga de la manera “pasada de moda”.

Lo resolví usando UILocalizedIndexCollation como se menciona en la pregunta NSFetchedResultsController vs UILocalizedIndexedCollation

La manera elegante es hacer que “firstLetter” sea una propiedad transitoria, SIN EMBARGO, en la práctica es lenta.

Es lento porque para calcular una propiedad transitoria, todo el objeto debe tener una falla en la memoria. Si tienes muchos registros, será muy, muy lento.

La manera rápida, pero poco elegante, es crear una propiedad “transitoria” no transitoria que actualice cada vez que configure su propiedad de “nombre”. Varias maneras de hacer esto: anular el asesor “setName:”, anular “willSave”, KVO.

Vea mi respuesta a una pregunta similar aquí , en la que describo cómo crear índices de sección localizada que son persistentes (porque no puede clasificar atributos transitorios en NSFetchedResultsController).

Aquí está la solución simple para obj-c, varios años y un lenguaje tarde. Funciona y funciona rápidamente en un proyecto mío.

Primero creé una categoría en NSString, nombrando el archivo NSString + Indexación. Escribí un método que devuelve la primera letra de una cadena

 @implementation NSString (Indexing) - (NSString *)stringGroupByFirstInitial { if (!self.length || self.length == 1) return self; return [self substringToIndex:1]; } 

Luego utilicé ese método en la definición del controlador de resultados obtenidos de la siguiente manera

 _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"name.stringGroupByFirstInitial" cacheName:nil]; 

El código anterior junto con los dos métodos de stringIndex funcionarán sin necesidad de jugar con propiedades transitorias o almacenar un atributo separado que solo contenga la primera letra en los datos centrales

Sin embargo, cuando trato de hacer lo mismo con Swift arroja una excepción, ya que no le gusta tener una función como ruta de clave de parte (o una propiedad de cadena calculada, lo intenté también) Si alguien sabe cómo lograr lo mismo en Swift me gustaría saber cómo.