【问题标题】:Can UITableViewDiffableDataSource detect an item changed?UITableViewDiffableDataSource 可以检测到更改的项目吗?
【发布时间】:2020-07-19 06:00:42
【问题描述】:

(这个问题是在下面与@AndreasOetjen讨论后重写的。感谢他的cmets。)

我在将 UITableView 与 diffable 数据源一起使用时遇到了问题。在我的应用程序中,当用户修改一个项目时,它可能会更改同一表格视图中显示的另一个项目。问题是,在我创建并应用包含两个项目的新值的新快照后,间接更改的项目的 UI 没有更新。

起初我认为 diffable 数据源能够在不同的快照中检测项目的值变化。例如,它可能以这种方式工作:如果它发现两个快照包含相同的项目(即两个快照中的项目具有相同的哈希值),它会比较它们的值并在值更改时更新表视图中该项目的行。但是,我后来意识到它可能不是那样工作的,因为 diffable 数据源没有定义任何 API 来获取和比较项目值(我最初的想法是它使用了 description 计算属性和 == 操作,但现在我相信这不是真的)。​​

所以我目前的理解是 diffable 数据源使用 item 的 hash 来检测 item order change(即插入新 item,旧 item 仍然存在等),而不是 item value更改(即,旧项目仍然存在,但其值已更改)。如果这个理解是正确的,那么它就引出了一个问题:如何使用 diffable 数据源来实现以下场景?

  • 一个项目有几个属性。一个属性(我们称之为属性 A)显示在 UI 中,但不用于生成哈希。
  • 该项目在旧快照和新快照中都存在,但其属性 A 发生了变化。所以它的 UI 需要更新。

在旧的UITableView API 中,这可以通过调用reloadRows()reloadData() 来实现。但是如何使用 diffable 数据源呢?

更新

在花时间做实验并解决问题后,我认为上述问题的理解是不正确的。请看下面我的回答。我相信这解释了可区分数据源的工作原理。我希望它对其他有同样困惑的人有所帮助。我很高兴被证明是错误的。真的。因此,如果您有不同的想法,请留下您的答案。

【问题讨论】:

    标签: ios uitableview uicollectionview uikit diffabledatasource


    【解决方案1】:

    经过近一天毫无头绪的实验,我相信我弄明白了 diffable 数据源的工作原理,并基于这种理解解决了我的问题(结果证明我最初的想法几乎是正确的)。

    Diffable 数据源使用项目哈希来识别项目。 对于同时存在于新旧快照中的同一个项目,可区分数据源通过对其旧值和新值执行“==”操作来检查项目是否发生变化。

    一旦弄清楚,它看起来是非常明显和简单的方法。但它是如此基础,以至于我无法理解为什么没有在任何地方明确提及它。

    所以,回答我原来的问题,是的,diffable 数据源可以检测项目值的变化。也就是说,当项目值是引用类型和/或行中显示的文本是该对象引用的对象的属性(例如,Core Data 中的关系)等时,就会变得棘手。

    另一个注释。无论是使用整个 item 结构还是仅使用其中的一部分来生成 item 哈希都无关紧要,只要它标识了 item。我更喜欢只使用真正识别它的项目的基本部分。

    【讨论】:

    • @matt 到处都提到Hashable 在swift 中需要Equatable。但它在任何地方都没有提到== 是如何在可区分数据源中使用的。在 WWDC 2019 session 220 视频中的示例代码中,== func 的定义与hash(into:) func 基本相同。在我看来,这是非常具有误导性的。除非我完全误解它,否则 diffable 数据源实际上可以很好地处理两个项目具有相同哈希值但不相等的情况。我相信这是 diffable 数据源设计的一部分,演讲者真的应该明确提到它。
    • 我换一种说法。 diffable 数据源的所有部分标识符或行标识符,就像 Set 的所有元素一样,必须是唯一的。唯一意味着它们都不等于任何其他。那是==。这就是数据源如何识别对象的本质。这就是为什么这些被称为标识符!如果一个对象在第 1 行,而与其相等的对象现在在第 3 行,则它们必须是同一个对象。 — Hashability 只是一种工具,可以或多或少地立即做出决定,而不必每次都检查其他所有值。
    • 昨天让我很困惑,我根据我的实验制定了一个关于 diffable 数据源如何工作的理论并解决了这个问题。在我的理解中,hash 用作项目标识符,== 用于检测更改。我看到您以相反的方式认为它(尽管您没有说哈希是用于检测更改)。我希望你是对的,因为如果它有效,那可能会更合理。但我不确定,而且似乎很难设计实验来证明哪种理解是正确的。
    • 如果数据源中的对象来自NSFetchedResultController,因此是NSManagedObject的子类,你有解决办法吗?
    • 再想一想,定义一个自定义的== 运算符可能会有所帮助(尽管我没有在我的应用程序中这样做)。
    【解决方案2】:

    我对你的最后一句话有点困惑:你写 my item is an enum with associated values of reference type,但在上面的示例中,你使用struct Book,这是一个值类型。无论如何,在任何情况下都必须牢记以下几点:

    散列是关于“对象”身份的。这只是一种改进身份比较、折叠等的捷径。

    如果您提供自定义哈希实现,两个对象 ab 必须以 a == b 暗示 hash(a) == hash(b) 的方式运行(反过来几乎总是如此,但可能存在冲突 - 尤其是弱哈希算法 - 如果不是这种情况)。

    因此,如果您只对titleauthor 进行哈希处理,那么您必须以一种也只比较titleauthor 的方式实现比较运算符。那么,如果notes发生变化,无论是数据源还是任何主体都不会检测到身份变化。

    UITableViewDiffableDataSource 是一种促进视图和数据源之间插入/删除语句同步的方法。如果你有这个

    *** 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无效更新:无效的节数。更新后表视图中包含的节数(3)必须等于更新前表视图中包含的节数(3),加上或减去插入或删除的节数(0插入,2已删除)。'

    那么一个可区分的数据源就是你的朋友。

    我希望这会有所帮助。

    【讨论】:

    • 嗨@@AndreasOetjen 感谢您的回答,很抱歉造成混淆(它们是两个独立的示例。我应该使用不同的名称)。恐怕我可能没有说清楚我对什么感到困惑。让我这样说吧。在旧的 UITableViewDataSource API 中,如果更改数据源中的一项,他可以调用 UITableView.reloadRows()。 diffable 数据源 API 中的等效方法是什么?新的 API 是否支持具有特定标识符的项目的值发生变化的概念? (我在新 API 中看不到这一点)。
    • 哦,等等……也许 diffable 数据源并不真正关心 item 的值,对吧?似乎它只关心项目订单更改,这就是它需要项目标识符的原因。至于 item 的 value,它只在 cellProvider 闭包中用户提供的代码中使用。在我的应用程序中,当用户在表格视图中编辑一项时,它可能会更改表格视图中的另一项。问题在于,虽然另一个项目在数据层中发生了更改,但它的新值并未在 UI 层中更新。似乎它可能是由 diffable 数据源以外的其他问题引起的?
    • 您通常不会调用reloadData,而是使用NSDiffableDataSourceSnapshotdataSource.apply。 diffable 数据源关心节和行的更改、插入和删除。这可能值得一读:wwdcbysundell.com/2019/diffable-data-sources-first-lookmedium.com/@alfianlosari/…
    • 感谢您的指点。我知道如何使用它。我刚刚更新了我的问题以使其更清楚。谢谢。
    • 但这不是违背了可区分数据源的目的吗? (实际上,在我的应用程序中确定哪些项目作为副作用被更改有点困难)。此外,我刚刚尝试了我的问题中描述的方法(将“属性 A”添加到哈希中),但它没有用。我检查了哈希值,它在两个快照中不同,但它的 UI 没有更新(我没有使用示例结构。我的应用程序中的一个是包含 NSManagedObject 作为其关联值的枚举)。我仍在试图找出问题所在。叹。如果我无法确定根本原因,我可能不得不求助于 reloadData()。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-03
    • 1970-01-01
    • 2017-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多