从关系中观察 sectionNameKeyPath 的变化

Observe to sectionNameKeyPath changes when from relationship

什么是确保对 sectionKeyPathName 值(来自相关实体)的更改传播到 UITableView 的 header 部分(包括重绘部分 header 是否具有新值并根据需要更改部分顺序)?

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

  1. NSFetchedResultsController 正在观察一个实体(Recipe 在我的例子中是 table 行)而不是 [=10= 的实体] (类别 的名字 属性 对我来说。)
  2. NSFetchedResultsController 委托的 controller(_:didChange:atSectionIndex:for) 从不 returns 移动 更新 其类型参数

背景

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

我有一个 table 食谱视图,每个类别都有一个部分。 header 部分是类别的名称。我使用了 fetchedResultsController 来填充 table 视图。一切都按预期工作(即,当食谱被更新、添加、删除或更改其类别时,table 视图反映了这些更改),只有一个例外。当类别名称更改时,我无法更改 headers 部分。我不仅希望 header 反映更改后的名称,而且还希望根据需要反映 re-sort。

代码片段

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

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

尝试失败

我假设我需要 观察 类别的 name 属性 以了解变化。最坏的情况是,我的部分需要 re-ordering。因此,如果 name 改变了,我会再次 performFetch() 然后 reloadData() 在 table 上。我试过这个技巧。

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

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

可能的解决方案:

1) 列出类别而不是食谱

即使 fetchedResultsController 被设计为在带有 tableview 或 collectionView 的 1:1 设置中很好地工作(即 fetchedResultsController 的 indexPath 等于视图的 indexPath)它不需要工作方法。

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

如果您有过滤食谱的谓词,这会变得更加困难。

2) 当有变化时触发fetchedResultsController

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

3) 有两个 fetchedResultsController

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

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