我正在使用Core Data作为表格视图,我想使用每个结果的第一个字母作为节标题(所以我可以在侧面获得节索引)。有没有办法用关键路径做到这一点?类似于下面的内容,我使用name.firstLetter
作为sectionNameKeyPath
(遗憾的是,这不起作用)。
我是否必须手动抓取每个结果的第一个字母并创建我的部分?放入一个新属性来保存第一个字母并将其用作sectionNameKeyPath
更好吗?
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext
sectionNameKeyPath:@"name.firstLetter"
cacheName:@"Root"];
感谢。
**编辑:**我不确定它是否有所作为,但我的结果是日语,按片假名排序。我想用这些片假名作为部分索引。
答案 0 :(得分:79)
您应该只将“name”作为sectionNameKeyPath传递。请参阅此answer以查询“使用索引编制核心数据支持的UITableView”。
<强>更新强>
只有你只关心快速索引标题滚动条时,该解决方案才有效。在这种情况下,您不会显示节标题。请参阅下面的示例代码。
否则,我同意reffgentis,即瞬态属性是最佳解决方案。此外,在创建NSFetchedResultsController时,sectionNameKeyPath具有此限制:
如果此键路径不相同 由第一种排序指定的 fetchRequest中的描述符,它们必须 生成相同的相对排序。 例如,第一个排序描述符 在fetchRequest中可以指定密钥 持久性财产; sectionNameKeyPath可能指定一个键 对于从中派生的瞬态属性 持久性属性。
使用NSFetchedResultsController的Boilerplate UITableViewDataSource实现:
- (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];
//}
更新2
对于新的“uppercaseFirstLetterOfName”瞬态属性,将新的字符串属性添加到模型中的适用实体,并选中“瞬态”框。
有几种方法可以实现getter。如果要生成/创建子类,则可以将其添加到子类的实现(.m)文件中。
否则,你可以在NSManagedObject上创建一个类别(我把它放在我的视图控制器的实现文件的顶部,但你可以在它自己的正确的头文件和实现文件之间拆分):
@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
此外,在此版本中,不要忘记将'uppercaseFirstLetterOfName'作为sectionNameKeyPath传递:
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext
sectionNameKeyPath:@"uppercaseFirstLetterOfName" // this key defines the sections
cacheName:@"Root"];
并且,要在UITableViewDataSource实现中取消注释tableView:titleForHeaderInSection:
:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo name];
}
答案 1 :(得分:11)
可能有一种更优雅的方式来做到这一点,但我最近遇到了同样的问题,并提出了这个解决方案。
首先,我在我正在索引的对象上定义了一个名为firstLetterOfName的瞬态属性,并将getter写入该对象的.m文件中。 e.x。
- (NSString *)uppercaseFirstLetterOfName {
[self willAccessValueForKey:@"uppercaseFirstLetterOfName"];
NSString *stringToReturn = [[self.name uppercaseString] substringToIndex:1];
[self didAccessValueForKey:@"uppercaseFirstLetterOfName"];
return stringToReturn;
}
接下来,我设置我的获取请求/实体以使用此属性。
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];
旁注,没有任何结果:小心使用NSFetchedResultsController - 它还没有完全被烘焙,但恕我直言,除了文档中列出的简单案例之外的任何情况,你可能会以“老式”的方式做得更好。
答案 2 :(得分:4)
我使用NSFetchedResultsController v.s. UILocalizedIndexedCollation问题中提到的UILocalizedIndexCollation解决了这个问题
答案 3 :(得分:1)
优雅的方法是让“firstLetter”成为一种短暂的属性,但在实践中却很慢。
它很慢,因为要计算瞬态属性,整个对象需要出错才能进入内存。如果你有很多记录,它会非常非常慢。
快速但不优雅的方法是创建一个非瞬态“firstLetter”属性,每次设置“name”属性时都会更新该属性。有几种方法:覆盖“setName:”评估者,覆盖“willSave”,KVO。
答案 4 :(得分:1)
请参阅我对类似问题here的回答,其中我描述了如何创建持久化的本地化sectionKey索引(因为您无法对NSFetchedResultsController中的瞬态属性进行排序)。
答案 5 :(得分:0)
这是obj-c的简单解决方案,几年和一种语言迟到。它在我的项目中工作和运作很快。
首先,我在NSString上创建了一个类别,命名文件NSString + Indexing。我写了一个返回字符串
的第一个字母的方法@implementation NSString (Indexing)
- (NSString *)stringGroupByFirstInitial {
if (!self.length || self.length == 1)
return self;
return [self substringToIndex:1];
}
然后我在fetched result controller的定义中使用了这个方法,如下所示
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"name.stringGroupByFirstInitial"
cacheName:nil];
以上代码与两个stringIndex方法结合使用,无需弄乱瞬态属性或存储仅包含核心数据中第一个字母的单独属性
然而,当我尝试用Swift做同样的事情时,它抛出异常,因为它不喜欢将函数作为部分键路径(或者计算出的字符串属性 - 我也尝试过)如果有人知道如何实现在Swift中同样的事情我很想知道如何。