在iPhone上学习核心数据。核心数据似乎很少有用于填充表格视图的示例。 CoreDataBooks示例使用了部分,但它们是从模型中的完整字符串生成的。我想将核心数据表组织成一个姓氏的第一个字母,即地址簿。
我可以进入并为每个人创建另一个属性,即单个字母,以便充当分部,但这看起来很糟糕。
这是我开始的......诀窍似乎在愚弄sectionNameKeyPath
:
- (NSFetchedResultsController *)fetchedResultsController {
//.........SOME STUFF DELETED
// Edit the sort key as appropriate.
NSSortDescriptor *orderDescriptor = [[NSSortDescriptor alloc] initWithKey:@"personName" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:orderDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext
sectionNameKeyPath:@"personName" cacheName:@"Root"];
//....
}
答案 0 :(得分:62)
Dave DeLong的方法很好,至少在我的情况下,只要你省略了几件事。以下是它对我的影响:
添加新的可选字符串属性 到被称为的实体 “lastNameInitial”(或其他内容) 那个效果)。
将此属性设为瞬态。这个 意味着核心数据不会打扰 将其保存到您的数据文件中。这个 属性只存在于内存中, 当你需要的时候。
为此生成类文件 实体。
不要担心这个问题 属性。创建这个getter(这是 一半魔术,恕我直言)
// THIS ATTRIBUTE GETTER GOES IN YOUR OBJECT MODEL
- (NSString *) committeeNameInitial {
[self willAccessValueForKey:@"committeeNameInitial"];
NSString * initial = [[self committeeName] substringToIndex:1];
[self didAccessValueForKey:@"committeeNameInitial"];
return initial;
}
// THIS GOES IN YOUR fetchedResultsController: METHOD
// Edit the sort key as appropriate.
NSSortDescriptor *nameInitialSortOrder = [[NSSortDescriptor alloc]
initWithKey:@"committeeName" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:nameInitialSortOrder]];
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext
sectionNameKeyPath:@"committeeNameInitial" cacheName:@"Root"];
上一步:遵循Dave的初始步骤生成的问题,它在setPropertiesToFetch上因无效的参数异常而死亡。我已经记录下面的代码和调试信息:
NSDictionary * entityProperties = [entity propertiesByName];
NSPropertyDescription * nameInitialProperty = [entityProperties objectForKey:@"committeeNameInitial"];
NSArray * tempPropertyArray = [NSArray arrayWithObject:nameInitialProperty];
// NSARRAY * tempPropertyArray RETURNS:
// <CFArray 0xf54090 [0x30307a00]>{type = immutable, count = 1, values = (
// 0 : (<NSAttributeDescription: 0xf2df80>),
// name committeeNameInitial, isOptional 1, isTransient 1,
// entity CommitteeObj, renamingIdentifier committeeNameInitial,
// validation predicates (), warnings (), versionHashModifier (null),
// attributeType 700 , attributeValueClassName NSString, defaultValue (null)
// )}
// NSInvalidArgumentException AT THIS LINE vvvv
[fetchRequest setPropertiesToFetch:tempPropertyArray];
// *** Terminating app due to uncaught exception 'NSInvalidArgumentException',
// reason: 'Invalid property (<NSAttributeDescription: 0xf2dfb0>),
// name committeeNameInitial, isOptional 1, isTransient 1, entity CommitteeObj,
// renamingIdentifier committeeNameInitial,
// validation predicates (), warnings (),
// versionHashModifier (null),
// attributeType 700 , attributeValueClassName NSString,
// defaultValue (null) passed to setPropertiesToFetch: (property is transient)'
[fetchRequest setReturnsDistinctResults:YES];
NSSortDescriptor * nameInitialSortOrder = [[[NSSortDescriptor alloc]
initWithKey:@"committeeNameInitial" ascending:YES] autorelease];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:nameInitialSortOrder]];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext
sectionNameKeyPath:@"committeeNameInitial" cacheName:@"Root"];
答案 1 :(得分:54)
我想我还有另一种选择,这个选项在NSString上使用了一个类别......
@implementation NSString (FetchedGroupByString)
- (NSString *)stringGroupByFirstInitial {
if (!self.length || self.length == 1)
return self;
return [self substringToIndex:1];
}
@end
现在稍后,在构建你的FRC时:
- (NSFetchedResultsController *)newFRC {
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:awesomeRequest
managedObjectContext:coolManagedObjectContext
sectionNameKeyPath:@"lastName.stringGroupByFirstInitial"
cacheName:@"CoolCat"];
return frc;
}
这是我最喜欢的方法。更清洁/更容易实施。此外,您不必对对象模型类进行任何更改即可支持它。这意味着它可以在任何对象模型上工作,只要节名称指向基于NSString
的属性答案 2 :(得分:15)
以下是可能让它工作的方式:
不要担心此属性的setter。创造这个吸气剂(这是神奇的一半,恕我直言)
- (NSString *) lastNameInitial {
[self willAccessValueForKey:@"lastNameInitial"];
NSString * initial = [[self lastName] substringToIndex:1];
[self didAccessValueForKey:@"lastNameInitial"];
return initial;
}
在您的获取请求中,仅请求此PropertyDescription,就像这样(这是魔法的另一个四分之一):
NSDictionary * entityProperties = [myEntityDescription propertiesByName];
NSPropertyDescription * lastNameInitialProperty = [entityProperties objectForKey:@"lastNameInitial"];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:lastNameInitialProperty]];
确保您的获取请求仅返回不同的结果(这是魔法的最后四分之一):
[fetchRequest setReturnsDistinctResults:YES];
通过这封信订购您的结果:
NSSortDescriptor * lastNameInitialSortOrder = [[[NSSortDescriptor alloc] initWithKey:@"lastNameInitial" ascending:YES] autorelease];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:lastNameInitialSortOrder]];
执行请求,看看它给你的是什么。
如果我理解它是如何工作的,那么我猜它会返回一个NSManagedObjects数组,每个NSManagedObjects只有lastNameInitial属性加载到内存中,并且是一组不同的姓氏首字母。
祝你好运,并报告这是如何运作的。我只是把它从头顶上做起来想知道这是否有效。 =)答案 3 :(得分:8)
我喜欢Greg Combs的回答。我做了一些修改,以便通过将字符串转换为大写字母来使“Smith”和“smith”等字符串出现在同一部分中:
- (NSString *)stringGroupByFirstInitial {
NSString *temp = [self uppercaseString];
if (!temp.length || temp.length == 1)
return self;
return [temp substringToIndex:1];
}
答案 4 :(得分:6)
我一直遇到这个问题。我总是回到最好的解决方案是给实体一个真正的第一个初始属性。作为一个真实的字段提供更有效的搜索和排序,因为您可以将字段设置为索引。在首次导入/创建数据时,将第一个初始值拉出并用它填充第二个字段似乎并不是太多工作。您必须以任何方式编写初始解析代码,但是您可以为每个实体执行一次,而不是再次执行。缺点似乎是你每个实体(和索引)存储一个额外的字符,这可能是微不足道的。
另外一个注意事项。我回避修改生成的实体代码。也许我错过了一些东西,但是用于生成CoreData实体的工具并不尊重我可能放在那里的任何代码。我生成代码时选择的任一选项都会删除我可能进行的任何自定义。如果我用巧妙的小函数填充我的实体,那么我需要为该实体添加一堆属性,我无法轻易地重新生成它。
答案 5 :(得分:1)
swift 3
首先,创建NSString的扩展(因为CoreData基本上使用的是NSString)
extension NSString{
func firstChar() -> String{
if self.length == 0{
return ""
}
return self.substring(to: 1)
}
}
然后使用firstChar keypath排序,在我的例子中,lastname.firstChar
request.sortDescriptors = [
NSSortDescriptor(key: "lastname.firstChar", ascending: true),
NSSortDescriptor(key: "lastname", ascending: true),
NSSortDescriptor(key: "firstname", ascending: true)
]
最后 使用sectionNameKeyPath
的firstChar keypathlet controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: "lastname.firstChar", cacheName: "your_cache_name")
答案 6 :(得分:-1)
我想我有更好的方法来做到这一点。而不是使用瞬态属性,在视图中将出现。重新计算NSManagedObject的派生属性并保存上下文。更改后,您可以重新加载表视图。
下面是计算每个顶点边数的示例,然后根据边数对顶点进行排序。在此示例中,Capsid是顶点,触摸是边缘。
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
[self.tableView reloadData];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Capsid"];
NSError *error = nil;
NSArray *results = [self.managedObjectContext executeFetchRequest:request error:&error];
if (error) {
NSLog(@"refresh error");
abort();
}
for (Capsid *capsid in results) {
unsigned long long sum = 0;
for (Touch *touch in capsid.vs) {
sum += touch.count.unsignedLongLongValue;
}
for (Touch *touch in capsid.us) {
sum += touch.count.unsignedLongLongValue;
}
capsid.sum = [NSNumber numberWithUnsignedLongLong:sum];
}
if (![self.managedObjectContext save:&error]) {
NSLog(@"save error");
abort();
}
}
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil) {
return __fetchedResultsController;
}
// Set up the fetched results controller.
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Capsid" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
// NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:NO];
// NSSortDescriptor *sumSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"sum" ascending:NO];
// NSArray *sortDescriptors = [NSArray arrayWithObjects:sumSortDescriptor, nil];
[fetchRequest setReturnsDistinctResults:YES];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"sum" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return __fetchedResultsController;
}