NSFetchedResultsController 并正确分组

Posted

技术标签:

【中文标题】NSFetchedResultsController 并正确分组【英文标题】:NSFetchedResultsController and getting the grouping right 【发布时间】:2013-04-17 15:24:48 【问题描述】:

我有以下问题。我有一个由 NSFetchedResultsController 提供支持的 UITableview。 它应该显示一个项目、文件夹和文件。没有比这更大的等级制度了。所以一个项目有几个文件夹,那些有几个文件。 现在我的表格视图应该显示这些文件,按文件夹分组。

所以我认为我的获取请求的谓词只是:

[NSPredicate predicateWithFormat:@"folder.project = %@", self.project];

然后我会使用 MagicalRecord 来制作一个 NSFetchedResultsController,方法如下:

[File fetchAllGroupedBy:@"folder.name"
          withPredicate:sectionPredicate
               sortedBy:@"folder.name"
              ascending:YES];

这基本上适用于一个大问题......如果文件夹中没有文件,我不会得到该文件夹​​的部分(我需要它!!!!)

所以我需要的是 tableview 来显示空文件夹的部分标题:

Folder A
  File 1
  File 2
Folder B
Folder C
  File 3

我可以在没有 NSFetchedResultsController 的情况下做到这一点,但我喜欢它很好地处理行和节的插入/删除,并且它可以观察变化......

感谢您的帮助, 干杯, 乔治

【问题讨论】:

我不认为有一个简单(甚至中等复杂)的解决方案。 FRC 不会创建空部分。您将不得不修改表视图数据源方法,但随后 FRC 索引路径和表视图索引路径不再同步,这使得 FRC 委托方法(didChangeObject,...)变得复杂。 【参考方案1】:

解决方案确实有点复杂。我将尝试解释部分解决方案(无嵌套文件夹,不支持部分排序​​顺序)

模型更改: 1) 将Folder 的父实体设置为File(如果使用虚拟项目则不需要)。 2) 将 BOOL 字段 isFinal 添加到 File 实体。 3) 为实体生成类文件。 4) 在Folder.h 中像这样实现-awakeFromInsert

- (void) awakeFromInsert

    self.isFinal = YES;
    self.folder = self;//You could also fabricate a dummy item

FetchedResultsController: 1) 像这样设置您的获取请求:

NSFetchRequest* request = [[NSFetchRequest alloc] initWithEntityName:@"File"];
NSSortDescriptor* terminalSort = [NSSortDescriptor sortDescriptorWithKey:@"isFinal" ascending:YES];
NSSortDescriptor* nameSort = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
[request setSortDescriptors:@[terminalSort,nameSort]];
[request setPredicate:[NSPredicate predicateWithFormat:@"folder.project == %@",project]]
//Any additional settings

2) 像这样初始化您的 FRC:

self.fetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                    managedObjectContext:self.managedObjectContext
                                      sectionNameKeyPath:@"folder"//or @"folder.<some unique property>"
                                               cacheName:nil];

注意sectionNameKeyPath 设置为folder,而不是folder.name 以支持同名文件夹(如果不需要,并且文件夹名称是唯一的,请使用folder.name

3) 在你的-controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: 方法的开头添加:

File* file = (File*)anObject;
if (file.isFinal) 
    return;

表格视图: 1) 实施:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

    id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
    return [sectionInfo numberOfObjects] - 1;

2) 实施:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section

    id<NSFetchedResultsSectionInfo> sec = [self.fetchedResultsController sections][section];

    return [[[[sec objects] objectAtIndex:0] folder] name];

这应该为您提供所需的基本功能。 为了支持更复杂的行为,需要进行额外的更改。

【讨论】:

嗨,丹,感谢您回答我的问题!所以基本上你为每个文件夹插入一个虚拟项目,这是他自己?无法对我的部分进行排序是很糟糕的......不可能改变它,会有吗? 正如我所说,为了按文件夹名称对部分进行排序,文件夹名称必须是唯一的,您 sectionNameKeyPath 将是 folder.name,或者您需要创建一个唯一的一个文件夹的属性,以匹配您所需的排序顺序并使其成为您的sectionNameKeyPath 啊,好吧...好吧,我确实尝试了这个解决方案,并会检查我是否喜欢它。我必须更改我的数据模型以便 ViewController 可以按照他想要的方式显示数据这一事实对我来说似乎并不好。我有一个可以使用多个 FRC 的 TableViewController 版本,我想我现在会坚持下去。当 name:NSManagedObjectContextObjectsDidChangeNotification 被触发时,我只是更新那些 FRC。我以这种方式失去了我的动画,但我会想出一些办法。但这样我就不必弄乱我的数据模型了。无论如何,非常感谢您的帮助!

以上是关于NSFetchedResultsController 并正确分组的主要内容,如果未能解决你的问题,请参考以下文章

在 Core Data 应用程序中调用 performFetch 后,是不是需要手动更新表视图?