核心数据:具有多个NSFetchedResultControllers的UITableView

ind*_*gie 26 iphone cocoa-touch core-data uitableview nsfetchedresultscontroller

我想做的很简单.在我的UITableViewController中,我想从多个NSFetchedResultControllers加载数据(我的数据模型中有多个实体),并将每个数据放入表视图中的不同部分.因此,例如,来自第一个NSFetchedResultController的所有获取的项目将在UITableView中的第0部分中,从另一个中获取的项目将进入第1部分,等等.

核心数据模板项目未演示如何执行此操作.所有内容(主要是索引路径)都是在不考虑部分的情况下编码的(默认模板中没有部分),所有内容都来自单个NSFetchedResultController.是否有任何示例项目或文档可以证明这样做?

谢谢

Gia*_*iao 25

假设你的标题中有以下内容(下面的代码会略显草率,我的道歉):

NSFetchedResultsController *fetchedResultsController1; // first section data
NSFetchedResultsController *fetchedResultsController2; // second section data
Run Code Online (Sandbox Code Playgroud)

让表知道你想要有两个部分:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 2; // you wanted 2 sections
}
Run Code Online (Sandbox Code Playgroud)

给它部分标题:

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
    return [NSArray arrayWithObjects:@"First section title", @"Second section title", nil];
}
Run Code Online (Sandbox Code Playgroud)

让表知道每个部分有多少行:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (section == 0) {
        return [[fetchedResultsController1 fetchedObjects] count];
    } else if (section == 1) {
        return [[fetchedResultsController2 fetchedObjects] count];
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

构建单元格:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    ... // table cell dequeue or creation, boilerplate stuff

    // customize the cell
    if (indexPath.section == 0) {
        // get the managed object from fetchedResultsController1
        // customize the cell based on the data
    } else if (indexPath.section == 1) {
        // get the managed object from fetchedResultsController2
        // customize the cell based on the data
    }

    return cell;
}
Run Code Online (Sandbox Code Playgroud)


mal*_*hal 8

多个 fetch 控制器(可能还有多个实体)是错误的方法。正确的解决方案是使用sectionNameKeyPathparam toNSFetchedResultController将结果分组为多个部分。如果您以不同的方式考虑您的实体,也许它们实际上是同一个实体,而您可以使用一个属性itemType,然后您可以对其进行分区(并且您也必须对其进行排序)。例如说我有实体 Hops 和 Grains 然后我可以将它们更改为 Ingredient 并有一个 int_16 属性ingredientType,然后我在代码中有一个枚举来存储值hopType = 0grainType = 1。毕竟成分只是一个名称和一个重量,这两者都是共享的。

然而,如果您的实体确实具有一组不同的属性,那么正确的解决方案是创建一个父抽象实体,该实体具有您可以用来分割的属性,例如sortOrder,sectionIDtype。然后,当您创建一个提取控制器并提取抽象父实体时,您实际上会获得包含所有子实体的结果。例如,在 Notes 应用程序中,他们有一个抽象的父实体NoteContainer,其中包含子实体AccountFolder. 这使单个提取控制器能够在该部分的第一个单元格中显示帐户,然后在以下单元格中显示所有文件夹。例如,所有 iCloud 备忘录(实际上是帐户),然后是备忘录(是默认文件夹),然后是所有自定义文件夹,然后是垃圾文件夹。他们使用一个sortOrder属性和默认文件夹是1,自定义文件夹都是2,垃圾箱是3。然后通过将其添加为排序描述符,他们可以按照他们想要的顺序显示单元格。它与您的要求有点不同,因为它们将 2 个实体混合到不同的部分,但您仍然可以仅使用不同的排序属性来使用它。

这个故事的寓意是不要与框架作斗争,拥抱它:-)


小智 5

使用两个NSFetchedResultsControllers扩展Giao的解决方案 - 我们必须记住我们的NSFetchedResultsController不知道我们的两个部分,并且返回的NSIndexPathes将始终用于第一部分.

所以当我们在单元格配置中获取对象时:

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (!cell) {
        [tableView registerNib:[UINib nibWithNibName:@"cell" bundle:nil] forCellReuseIdentifier:@"cell"];
        cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    }
    [self configureCell:cell atIndexPath:indexPath];
    return cell;
}

-(void)configureCell:(UITableViewCell*)cell atIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 0) {
        NSManagedObject *object = [self.fetchedResults1 objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]];
        //use object to configure cell
    } else {
        NSManagedObject *object = [self.fetchedResults2 objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]];
        //use object to configure cell
    }
}
Run Code Online (Sandbox Code Playgroud)

在NSFetchedResultsController注意到一些更改时更新单元格:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
    NSIndexPath *customIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:(controller == self.fetchedResultsController1)?0:1];
    NSIndexPath *customNewIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row inSection:(controller == self.fetchedResultsController2)?0:1];
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:customNewIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:customIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;
        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:customIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:customNewIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}
Run Code Online (Sandbox Code Playgroud)