Como usar o primeiro caractere como um nome de seção

Estou usando o Core Data para uma visualização de tabela e gostaria de usar a primeira letra de cada um dos meus resultados como o header da seção (para obter o índice da seção ao lado). Existe uma maneira de fazer isso com o caminho da chave? Algo como abaixo, onde eu uso name.firstLetter como sectionNameKeyPath (infelizmente isso não funciona).

Eu tenho que pegar a primeira letra de cada resultado manualmente e criar minhas seções assim? É melhor colocar uma nova propriedade para apenas segurar a primeira letra e usar isso como sectionNameKeyPath ?

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

Obrigado.

** EDIT: ** Não tenho certeza se isso faz diferença, mas meus resultados são japoneses, classificados por Katakana. Eu quero usar esses Katakana como o índice de seção.

Você deve apenas passar “nome” como sectionNameKeyPath. Veja esta resposta à pergunta “Core Data apoiado UITableView com indexação”.

ATUALIZAR
Essa solução só funciona se você só se importa em ter o scroller de título de índice rápido. Nesse caso, você NÃO exibiria os headers da seção. Veja abaixo o código da amostra.

Caso contrário, concordo com a refulgência de que uma propriedade transitória é a melhor solução. Além disso, ao criar o NSFetchedResultsController, a sectionNameKeyPath tem esta limitação:

Se esse caminho de chave não for o mesmo que o especificado pelo primeiro descritor de sorting em fetchRequest, eles deverão gerar os mesmos ordenamentos relativos. Por exemplo, o primeiro descritor de sorting em fetchRequest pode especificar a chave para uma propriedade persistente; sectionNameKeyPath pode especificar uma chave para uma propriedade transitória derivada da propriedade persistente.

Boilerplate UITableViewDataSource implementações usando 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]; //} 

ATUALIZAÇÃO 2

Para a nova propriedade transitória ‘uppercaseFirstLetterOfName’, inclua um novo atributo de string na entidade aplicável no modelo e marque a checkbox “transitório”.

Existem algumas maneiras de implementar o getter. Se você está gerando / criando subclasss, então você pode adicioná-lo no arquivo de implementação (.m) da subclass.

Caso contrário, você pode criar uma categoria em NSManagedObject (eu coloquei isso logo no topo do arquivo de implementação do meu view controller, mas você pode dividi-lo entre um header próprio e um arquivo de implementação próprio):

 @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 

Além disso, nesta versão, não esqueça de passar ‘uppercaseFirstLetterOfName’ como sectionNameKeyPath:

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

E, para descomentar tableView:titleForHeaderInSection: na implementação de UITableViewDataSource:

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

Pode haver uma maneira mais elegante de fazer isso, mas recentemente tive o mesmo problema e criei essa solução.

Primeiro, defini uma propriedade transitória nos objects que estava indexando, chamada firstLetterOfName, e escrevi o getter no arquivo .m do object. ex

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

Em seguida, configurei minhas solicitações / entidades de busca para usar essa propriedade.

 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 lateral, a propósito de nada: Tenha cuidado com NSFetchedResultsController – não é exatamente totalmente cozido ainda IMHO, e qualquer situação além dos casos simples listados na documentação, você provavelmente será melhor fazê-lo da maneira ‘à moda antiga’.

Eu resolvi isso usando o UILocalizedIndexCollation como mencionado na pergunta NSFetchedResultsController vs UILocalizedIndexedCollation

A maneira elegante é fazer da “primeira carta” uma propriedade transitória, mas na prática isso é lento.

É lento porque, para que uma propriedade transitória seja calculada, todo o object precisa ser colocado em falha na memory. Se você tiver muitos registros, será muito, muito lento.

A maneira rápida, mas deselegante, é criar uma propriedade “firstLetter” não transitória que você atualiza toda vez que define sua propriedade “name”. Várias maneiras de fazer isso: sobrescrever o assessor “setName:”, replace “willSave”, KVO.

Veja minha resposta a uma pergunta semelhante aqui , na qual descrevo como criar índices sectionKey localizados que são persistentes (porque você não pode classificar atributos transitórios em um NSFetchedResultsController).

Aqui está a solução simples para obj-c, vários anos e uma língua atrasada. Ele funciona e trabalha rapidamente em um projeto meu.

Primeiro criei uma categoria em NSString, nomeando o arquivo NSString + Indexing. Eu escrevi um método que retorna a primeira letra de uma string

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

Então eu usei esse método na definição do controlador de resultado buscado da seguinte forma

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

O código acima em conjunto com os dois methods stringIndex funcionará sem a necessidade de mexer com propriedades transitórias ou armazenar um atributo separado que apenas mantenha a primeira letra nos dados principais

No entanto, quando tento fazer o mesmo com o Swift, ele lança uma exceção, pois não gosta de ter uma function como parte do caminho da chave (ou uma propriedade de string calculada – eu tentei também) Se alguém lá fora sabe como conseguir a mesma coisa em Swift eu gostaria muito de saber como.