如何在不删除和插入 UITableViewDiffableDataSource 的情况下重新加载项目?

Tom*_*ing 11 objective-c uitableview diffabledatasource nsdiffabledatasourcesnapshot

我正在我的应用程序中使用UITableViewDiffableDataSource. 每个单元格代表一个搜索命中并在单元格标题中突出显示搜索匹配项,有点像 Xcode 的 Open Quickly 窗口突出显示其结果项的部分。在搜索字段中输入文本时,我更新了结果列表。结果随着相关性的变化在列表中上下移动。

诀窍是每次搜索文本更改时我都需要强制每个单元格重新呈现,因为新的搜索字符串意味着更新单元格标题的突出显示部分。但我不想动画删除和插入,因为它仍然是同一个项目。如何使用快照告诉数据源它需要重新加载单元格?

我这样声明数据源:

@property (retain) UITableViewDiffableDataSource<NSString *, SearchHit *> *dataSource;
Run Code Online (Sandbox Code Playgroud)

SearchHit代表一个搜索结果;它具有显示标题和要在标题中突出显示的范围数组的属性。并且它会覆盖hashandisEqual:以便唯一标识每个结果行。

我的代码看起来像这样:

-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
  NSArray<SearchHit *> *hits = [self fetchHits:searchText];
  NSDiffableDataSourceSnapshot<NSString *, SearchHit *> *snap = [[[NSDiffableDataSourceSnapshot alloc] init] autorelease];
  [snap appendSectionsWithIdentifiers:@[@""]];
  [snap appendItemsWithIdentifiers:hits];
  [snap reloadItemsWithIdentifiers:hits];
  [self.dataSource applySnapshot:snap animatingDifferences:YES];
}
Run Code Online (Sandbox Code Playgroud)

起初我没有在reloadItemsWithIdentifiers那里打电话,然后一旦它出现在结果列表中,任何单元格都不会改变。添加reload呼叫有帮助,但现在大多数单元格总是落后于一个更新。这在我的代码中的某处闻起来像是逻辑错误,但我已经验证传递给快照的命中是正确的,而传递给数据源的单元格创建回调的命中不是。

Donny Wals 的这篇文章和涉及 Steve Breen 的相关 Twitter 线程建议解决此问题的方法是使项目标识符类型仅表示显示单元格所需的属性。所以我更新了SearchHit的散列和相等比较以包含标题的突出显示部分,而他们以前没有。然后我在每次更新时删除并插入所有单元格的动画,这是我不想要的。

这看起来reloadItemsWithIdentifiers应该怎么做……对吧?

示例项目在这里GitHub上。

Chr*_*kle 6

diffable 数据源 API 可能不是在单元格本身上实现动画效果的正确工具。它\xe2\x80\x99s 适合细胞出现、消失和排序的动画。如果您的数据源有通过 Hashable 一致性表达的更改,API 会将其视为更改并删除/插入等。

\n\n

我的建议是从项目标识符中删除搜索文本,并让每个单元格观察搜索文本并独立于数据源实现动画或重绘。

\n

  • @SherwinZadeh 我在 WWDC 实验室与一位 Apple 工程师讨论了这个问题,他们似乎认为这是一个错误 - `reloadItemsWithIdentifiers` 应该处理这个问题。在它得到修复之前我只是不制作动画。 (5认同)
  • 我实际上不需要单元格本身的动画 - 我只需要显示更新而无需删除/插入动画。这不是 `reloadItemsWithIdentifiers` 的用途吗?我在 GitHub 上添加了示例代码来进行说明。 (2认同)

Tom*_*ing 4

正确的解决方案实际上是 API 的名称 - 您提供给数据源的对象应该是标识符,就像数据库中的 rowid 值一样。就我而言,当项目标识符不代表我可以查找的数据库中的行时,我只需要将对象的状态保留在某种查找结构中,这样当我调用 时,reloadItemsWithIdentifiers我就可以获得状态来自该结构的每个单元格,而不是来自数据源交给我的对象。