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

发布于 2024-08-22 01:02:31 字数 378 浏览 3 评论 0 原文

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

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

谢谢

What I want to do is pretty simple. In my UITableViewController, I want to load data from multiple NSFetchedResultControllers (I have multiple entities in my data model) and put data from each one into a different section in the table view. So for example, all the fetched items from the first NSFetchedResultController would go in section 0 in the UITableView, the fetched items from the other one goes into section 1, etc.

The Core Data template project doesn't demonstrate how to do this. Everything (mainly the index paths) is coded without taking sections into account (there are no sections in the default template) and everything is taken from a single NSFetchedResultController. Are there any example projects or documentation that demonstrates doing this?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

A君 2024-08-29 01:02:31

假设您的标题中有以下内容(下面的代码可能有点草率,我很抱歉):

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

让表格知道您想要有 2 个部分:

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

给它部分标题:

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

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

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

    return 0;
}

构建单元格:

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

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;
}
夜夜流光相皎洁 2024-08-29 01:02:31

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

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

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

Multiple fetch controllers (and possibly multiple entities) is the wrong approach. The correct solution is to use the sectionNameKeyPath param to the NSFetchedResultController to group the results into multiple sections. If you think about your entities differently perhaps they are actually the same entity and instead you can use a property itemType which you can then section on (and you must also sort on it too). E.g. say I had entities Hops and Grains then I could change those to Ingredient and have a int_16 property ingredientType which I then have an enum in code to store the values hopType = 0, grainType = 1. After all the ingredient is just a name and a weight, which both of these share.

If however your entities really have a distinct set of properties, then the correct solution is to create a parent abstract entity that has a property that you can use to section, e.g. sortOrder, sectionID or type. When you then create a fetch controller and fetch the abstract parent entity, you actually get results containing all of the sub-entities. E.g in the Notes app they have an abstract parent entity NoteContainer that has child entities Account and Folder. This enables a single fetch controller to display the account in the first cell in the section, and then have all the folders in the following cells. E.g. All iCloud Notes (is actually the account), then Notes (is the default folder), followed by all the custom folders, then the trash folder. They use a sortOrder property and the default folder is 1, the custom folders are all 2, and the trash is 3. Then by adding this as a sort descriptor they can have the cells display in the order they want. It's a bit different from your requirement because they have the 2 entities mixed into different sections, but you can still make use of it just with different sort properties.

The moral of the story is don't fight the framework, embrace it :-)

远昼 2024-08-29 01:02:31

使用两个 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;
    }
}

Extending Giao's solution with two NSFetchedResultsControllers - we have to remember our NSFetchedResultsController do not know about our two sections and returned NSIndexPathes will be always for first section.

So when we are getting an object in cell configuration:

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

Updating cells while NSFetchedResultsController noticed some changes:

- (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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文