Ios NSFetchedResultsController:如何从实体的多个关系中获取sectionName
我有这样一个核心数据模型:Ios NSFetchedResultsController:如何从实体的多个关系中获取sectionName,ios,core-data,nssortdescriptor,nsfetchedresultscontroller,Ios,Core Data,Nssortdescriptor,Nsfetchedresultscontroller,我有这样一个核心数据模型: ParentObject>ChildObject ParentObject和ChildObject都有一个属性levelNumber,如下所示: typedef enum { Primary, Secondary, Tertiary } LevelNumber; 我还有一种方法可以将ParentObject和childObject中的级别号从int转换为string: -(void) levelString { switch(self
ParentObject>ChildObject
ParentObject
和ChildObject
都有一个属性levelNumber
,如下所示:
typedef enum {
Primary,
Secondary,
Tertiary
} LevelNumber;
我还有一种方法可以将ParentObject和childObject中的级别号从int转换为string:
-(void) levelString
{
switch(self.levelNumber)
{
case Primary: return @"Primary";
case Secondary: return @"Secondary";
case Tertiary: return @"Tertiary";
default: return @"Error";
}
}
现在,我在一个表视图中有一个FetchedResultsController,其中列出了父对象
。
我想在节名中得到的是:
- 如果
是ParentObject
或Secondary
,则显示该部分 名称为treative
或二级
三级
- 如果
是ParentObject
,但Primary
中的任何childobject
是
或Secondary
,则将节名显示为treative
或Secondary
treative
- 如果
和所有ParentObject
都是childobject
,则将节名显示为Primary
Primary
ParentObject
的levelNumber
,这是非常简单的,如下所示-
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"ParentObject"];
NSSortDescriptor *levelNumSD = [NSSortDescriptor sortDescriptorWithKey:@"levelNumber" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObjects:levelNumSD, nil];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:myContext
sectionNameKeyPath:@"levelString"
cacheName:nil];
我理解FRC中的限制是SortDescriptor的结果应以希望显示的相同顺序返回结果。
我如何在这里合并
ChildObject
检查。它是一种新的排序描述符,还是其他什么?看看苹果的示例代码,该代码解释了如何将日期作为节,您也可以在您的案例中使用它。您在titleForSection
中管理的字符串的实际显示,但您在数据库中保留了一个“基本”且可排序的属性,称为sectionIdentifier
在您的特定情况下,节标识符就像levelNumber
一样,只需返回所有子项中最高的levelNumber
即可计算
模式如下:
-(NSString*)sectionIdentifier {
[self willAccessValueForKey:@"sectionIdentifier"];
NSNumber *tmp = [self primitiveSectionIdentifier];
[self didAccessValueForKey:@"sectionIdentifier"];
if (!tmp) {
NSNumber *childrenMax = [self valueForKeyPath:@"@max.children.levelNumber"];
tmp = childrenMax.intValue > self.levelNumber.intValue ?
childrenMax : self.levelNumber;
[self setPrimitiveSectionIdentifier:tmp];
}
return tmp;
}
如果实体发生变化,不要忘记重置它
-(void)setLevelNumber:(NSNumber)newNumber {
[self willChangeValueForKey:@"levelNumber"];
[self setPrimitiveLevelNumber:newNumber];
[self willChangeValueForKey:@"levelNumber"];
[self setPrimitiveSectionIdentifier:nil];
}
最后,当相关数据发生变化时,确保其无效:
+(NSSet*) keyPathsForValuesAffectingSectionIdentifier {
return [NSSet setWithObject:@"levelNumber"];
}
要监视任何子对象在
levelNumber
中的更改,请让父对象侦听NSManagedObjectContextDidSaveNotification
并查看其子对象是否在该保存中。IMO在父对象模型中有一个单独的属性sectionLevelNumber
。这可能会对您有所帮助。@Bala如果我理解正确,属性sectionLevelNumber
本身不会存储任何值,但它的getter实际上会查看ParentObject
的levelNumber
以及所有ChildObjects
并查看要返回的级别号。这将是一个问题,因为在FRC的第一次获取中,它期望值存储在数据库中,而不是在运行时派生。Mundi已经清楚地解释了这一点,这是一个很好的答案!但是我有一些问题:1)如果“levelNumber”在keypathsforvaluesafectingsectionidentifier
中注册,是否仍然需要覆盖setLevelNumber
?2) “@children”中的@
是打字错误还是故意的?3) 更改ChildObject的levelNumber真的会使相关ParentObject的sectionIdentifier无效吗?也许在ChildObject中重写setLevelNumber
会很有用。谢谢1) 是的,这是一个很好的做法,没有坏处。2) 打字错误。更正了。3) 原则上你是对的,但我会担心实体之间的相互依赖性。理想情况下,您只能将此功能封装到父实体中。但是,使用当前代码,修改ChildObject的levelNumber不会更新相关ParentObject的sectionIdentifier。因为keypathsforvaluesafectingsectionidentifier中的“children”只影响在“children”关系中添加/删除子对象,而不会修改已经相关的子对象(除非我弄错了)。这是正确的。毕竟,您必须转到子实体。最好覆盖setLevelNumber
。这不能完全起作用。请求.排序描述符应使用什么?。如果我只使用parentObject
的levelNumber
。它给出了错误:CoreData:error:(NSFetchedResultsController)索引2处的获取对象具有无序的节名“2(次要)对象必须按节名排序”
。例如,当所有的ParentObject
都是Primary
,但是第二个ParentObject
的ChildObject
中有一个是Secondary
时,就会发生这种情况。我不能使用sectionIdentifier
进行排序,因为它是一个临时属性。