28

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

Core Data 模板项目没有演示如何执行此操作。一切(主要是索引路径)都是在不考虑节的情况下编码的(默认模板中没有节),并且一切都取自单个 NSFetchedResultController。是否有任何示例项目或文档可以证明这样做?

谢谢

4

3 回答 3

26

Assume for a moment the following in your header (code below will be slightly sloppy, my apologies):

NSFetchedResultsController *fetchedResultsController1; // first section data
NSFetchedResultsController *fetchedResultsController2; // second section data

Let the table know you want to have 2 sections:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 2; // you wanted 2 sections
}

Give it the section titles:

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

Let the table know how many rows there are per sections:

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

    return 0;
}

Build the cell:

- (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;
}
于 2010-02-22T09:14:32.987 回答
9

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

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

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

于 2017-10-18T13:55:00.183 回答
5

用两个 NSFetchedResultsController 扩展 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
    }
}

在 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;
    }
}
于 2017-03-07T11:41:42.327 回答