带有由string的第一个字母创build的部分的NSFetchedResultsController

在iPhone上学习核心数据。 在核心数据填充表格视图的部分似乎有几个例子。 CoreDataBooks示例使用段,但是它们是从模型中的完整string生成的。 我想通过姓氏的第一个字母,地址簿,将核心数据表组织成部分。

我可以为每个人创造另一个属性,即一个单一的字母,以便作为部门分工,但是这似乎很糟糕。

这是我开始…伎俩似乎是愚弄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"]; //.... } 

戴夫德隆的方法是好的,至less在我的情况下,只要你省略了几件事情。 以下是它为我工作的方式:

  • 将一个新的可选string属性添加到名为“lastNameInitial”的实体(或者那个效果)。

    使这个属性暂时。 这意味着Core Data不会将其保存到数据文件中。 当你需要的时候,这个属性将只存在于内存中。

    为此实体生成类文件。

    不要担心这个属性的二传手。 创build这个吸气(这是魔术的一半,恕我直言)


 // 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带有一个无效参数exception的情况下生成了问题。 我已经logging下面的代码和debugging信息:

 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"]; 

我想我还有另一个select,这个使用NSString类别…

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

现在稍后,在构build您的FRC时:

 - (NSFetchedResultsController *)newFRC { NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:awesomeRequest managedObjectContext:coolManagedObjectContext sectionNameKeyPath:@"lastName.stringGroupByFirstInitial" cacheName:@"CoolCat"]; return frc; } 

这是我现在最喜欢的方法。 更清洁/更容易实施。 此外,您不必对对象模型类进行任何更改以支持它。 这意味着它可以在任何对象模型上工作,只要节名称指向基于NSString的属性

以下是您可以如何运作的方法:

  • 将一个新的可选string属性添加到名为“lastNameInitial”的实体(或者那个效果)。
  • 使这个属性暂时。 这意味着Core Data不会将其保存到数据文件中。 当你需要的时候,这个属性将只存在于内存中。
  • 为此实体生成类文件。
  • 不要担心这个属性的二传手。 创build这个吸气(这是魔术的一半,恕我直言)

      - (NSString *)lastNameInitial { 
    [self willAccessValueForKey:@“lastNameInitial”];
    NSString * initial = [[self lastName] substringToIndex:1];
    [self didAccessValueForKey:@“lastNameInitial”];
    返回初始;
    }
  • 在你的获取请求中,请求只有这个PropertyDescription,像这样(这是魔术的另一个四分之一):

      NSDictionary * entityProperties = [myEntityDescription propertiesByName]; 
    NSPropertyDescription * lastNameInitialProperty = [entityProperties objectForKey:@“lastNameInitial”];
    [fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:lastNameInitialProperty]];
  • 确保你的获取请求只返回不同的结果(这是魔术的最后一个季度):

      [fetchRequest setReturnsDistinctResults:YES]; 
  • 通过这封信订购你的结果:

      NSSortDescriptor * lastNameInitialSortOrder = [[[NSSortDescriptor alloc] initWithKey:@“lastNameInitial”升序:YES] autorelease]; 
    [fetchRequest setSortDescriptors:[NSArray arrayWithObject:lastNameInitialSortOrder]];
  • 执行请求,并看看它给你什么。

如果我明白这是如何工作的,那么我猜测它会返回一个NSManagedObjects数组,每个NSManagedObjects只有lastNameInitial属性加载到内存中,谁是一组不同的姓氏首字母缩写。

祝你好运,并报告如何工作。 我只是把它做成了我的头顶,想知道这是否有效。 =)

我喜欢Greg Combs的回答。 我做了一些修改,使得“Smith”和“smith”等string可以通过将string转换为大写字符出现在同一节中:

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

我一直遇到这个问题。 似乎最好的,我总是回来的解决scheme是给实体一个真正的第一个初始财产。 作为一个真正的领域提供更有效的search和sorting,因为你可以设置字段索引。 当数据首次被导入/创build时,似乎并不需要太多的工作来完成第一个初始化工作,并用它填充第二个字段。 您必须以任何方式编写初始parsing代码,但是您可以在每个实体中执行一次,而不再执行一次。 缺点似乎是你实际上每个实体存储一个额外的字符(和索引),这可能是微不足道的。

一个额外的笔记。 我害怕修改生成的实体代码。 也许我错过了一些东西,但用于生成CoreData实体的工具不尊重我可能放在那里的任何代码。 生成代码时select的选项可以删除我可能做出的任何自定义。 如果我用聪明的小函数填充我的实体,那么我需要添加一堆属性到该实体,我不能轻松地重新生成它。

迅速3

首先,创buildNSString的扩展(因为CoreData基本上是使用NSString)

 extension NSString{ func firstChar() -> String{ if self.length == 0{ return "" } return self.substring(to: 1) } } 

然后使用firstChar keypath进行sorting,在我的情况下是lastname.firstChar

 request.sortDescriptors = [ NSSortDescriptor(key: "lastname.firstChar", ascending: true), NSSortDescriptor(key: "lastname", ascending: true), NSSortDescriptor(key: "firstname", ascending: true) ] 

最后使用sectionNameKeyPath的firstChar keypath

 let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: "lastname.firstChar", cacheName: "your_cache_name") 

我想我有一个更好的方法来做到这一点。 将显示在视图中而不是使用瞬态属性。 重新计算NSManagedObject的派生属性并保存上下文。更改后,您可以重新加载表视图。

以下是计算每个顶点的边数的示例,然后按照边的数量对顶点进行sorting。 在这个例子中,Capsid是顶点,touch是边缘。

 - (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; }