从关系中观察到 sectionNameKeyPath 的变化

Posted

技术标签:

【中文标题】从关系中观察到 sectionNameKeyPath 的变化【英文标题】:Observe to sectionNameKeyPath changes when from relationship 【发布时间】:2017-08-13 00:53:01 【问题描述】:

确保sectionKeyPathName 值(来自相关实体)的更改传播到 UITableView 的部分标题的最佳做法是什么(包括使用新值重新绘制部分标题并在需要时更改部分排序) ?

我假设 NSFetchedResultsController 委托的当前功能无法满足我的需求,因为

    NSFetchedResultsController 正在观察一个实体(Recipe 在我的情况下是针对表格行)而不是 sectionKeyPathName 的实体(类别 的名称我的财产。) NSFetchedResultsController 委托的 controller(_:didChange:atSectionIndex:for) 永远不会为其类型参数返回 moveupdate

背景

我有两个实体,Category 和 Recipe,其中一个 Category 可以有多个 Recipes,而一个 Recipe 将只属于 1 个类别(又名 1:M 关系)。

我有一个食谱的表格视图,每个类别都有一个部分。部分标题是类别的名称。我使用了 fetchedResultsController 来填充表格视图。一切都按预期工作(即,当食谱更新、添加、删除或更改其类别时,表格视图会反映这些更改),但有一个例外。当类别名称更改时,我无法更改部分标题。我不仅希望标题反映更改的名称,还希望根据需要重新排序。

代码片段

我会根据需要分享。正如我所说,其他一切都正常,并且有很多代码。

FWIW - 我的 sectionKeyPathName 值与 sortDescriptor 数组的第一个值相同。

尝试失败

我假设我需要观察类别的name 属性以进行更改。最坏的情况是,我的部分需要重新排序。因此,如果name 更改了,我将再次performFetch(),然后将reloadData() 放在桌子上。我试过这种技术。

    观察NSManagedObjectContextWillSave 以确定类别(当前是我的表格视图中的一个部分)的name 属性是否发生变化。 如果name 属性发生变化,那么当我观察到NSManagedObjectContextDidSave 时,我将再次执行提取并重新加载数据()。这似乎解决了问题,但是当我测试将食谱移动到不同的类别时崩溃了。 (移动测试在此更改之前有效。)(崩溃为 An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of sections.

【问题讨论】:

【参考方案1】:

fetchedResultsController 很棒。但他们并不是超级聪明。他们仅监控设置为获取的实体的更改。它假定所有其他更改都不会影响它。所以如果你有一个基于关系的谓词或排序描述符,它可能会导致问题。

可能的解决方案:

1) 列出类别而不是食谱

尽管 fetchedResultsController 被设计为在 1:1 设置中与 tableview 或 collectionView 很好地工作(即 fetchedResultsController 的 indexPath 等于视图的 indexPath),但它不需要那样工作。

您可以为没有 sectionKeyPathName 的类别创建一个 fetchedResultsController。然后将节数设置为控制器中的项目数。每个部分中的项目数等于每个类别中的食谱数。

如果您有一个谓词来过滤食谱,这可能会变得更加困难。

2) 有变化时触发fetchedResultsController

如果您只在少数几个地方更改类别,则可能很容易简单地“弄脏”该类别中的所有配方以触发 fetchedResultsController。如果您将属性设置为等于自身(即 r.recipeId = r.recipeId),它将触发 fetchedResultsController 中的更新。因此,当您更改 categoryName 时,还会遍历所有食谱并将它们弄脏。

3) 有两个 fetchedResultsController

FetchedResultsController 是非常轻量级的对象,您不必害怕根据需要制作任意数量的对象。您可以创建两个。一份用于食谱,一份用于类别。这些部分基于 fetchedResultsController 类别。对于配方 fetchedResultsController,您可以根据类别的不变属性(例如 categoryId)对其进行分组。然后,当您想知道某个类别的 numberOfRows 时,您必须在配方 fetchedResultsController 中找到它。弄清楚 indexPath 可能有点烦人 - 但如果你创建一个单独的对象来管理它,这没什么大不了的。

如果您要显示所有食谱,我会推荐 #1。如果你对你的食谱有一个谓词,我会推荐#2。如果您是代码纯粹主义者并且希望完全分离模型和视图,我只会推荐#3。

【讨论】:

感谢您提供的解决方案选项。 列出类别似乎是在用一个问题换另一个问题。每当配方名称更改时,此解决方案是否会导致难以更新表?我正在尝试实现 Option 2,因为我已经有了跟踪类别名称更改的代码 - 这将是我可以触发 dirty-ing 函数的时候。需要明确的是,您认为我在任何时候都不需要在我的 resultsController 上再次 performFetch() 吗? 无需多次调用 performFetch。 fetchedResultsController 应该跟踪更改。

以上是关于从关系中观察到 sectionNameKeyPath 的变化的主要内容,如果未能解决你的问题,请参考以下文章

从UML到观察者模式

观察者模式

如何在核心数据关系真正改变之前观察它?

观察一对多关系中的依赖键

观察者模式

设计模式之观察者模式