SGListAnimator 1.0.1

SGListAnimator 1.0.1

David McNerneyJames VanasEd PauloskyMatt Baron 维护。



  • SeatGeek

TLDR

SGListAnimator 为您的表格和集合视图提供动画过渡,因此您无需调用 reloadData,这会让 UI 瞬间跳转至新的状态而没有任何动画。

问题所在

我们都在我们的 iOS 应用中使用表格和集合视图来显示事物列表。它们是伟大的组件,并且附带了一组动画更改的方法。但是,您需要先知道更改是什么,然后才能知道调用哪些动画方法。

如果更改是您应用程序推动的,那么知道它们很容易。然而,我们经常显示来自其他地方的列表,例如服务器。当我们收到更新后的列表时,我们知道它可能仅改变了一点点——这里添加了一行,那里进行了删除或移动。不幸的是,比较新旧列表以找到所有差异并不那么容易。如果您的算法错过了任何内容,则应用程序会崩溃。

如果您的表格视图由 Core Data 表支持,则可以使用 NSFetchedResultsController,它提供了可以映射到动画方法的代理回调。其他人大多只是放弃,并调用 reloadData

SGListAnimator

SGListAnimator 比较您的旧列表和新列表,检测不同类型的更改(有 7 种),并在一个或多个动画块中对表或集合中的适当动画方法进行调用。

需求

要使用它,您需要

  • 使用具有唯一标识符的对象表示您的部分 -- 标题字符串效果很好。
  • 使用也具有唯一标识符的对象表示您的行/项目。这意味着它们从 hashisEqual 返回适当的值。通常,您已经在使用符合要求的数据模型对象。
  • 不要有重复的部分或项目。
  • 将您的表格/集合视图的备份数据保存在数组属性中,这样当您有更新后的数据时,您可以方便地使用旧的和新的数组。

安装

您可以通过 Cocoapods 整合,使用 Git 子模块,或只需将此存储库中的相关文件复制到您的项目中。这些文件位于 SGListAnimator 文件夹中。

 target 'MyTarget' do
    pod 'SGListAnimator'
 end

示例代码

我们有一个方便的类方法来帮助您入门。您只需要保存一个您的部分标题数组,以及另一个数组,该数组包含每个部分的项

- (void)updateTableView {
  // Save a reference to the previous content
  NSArray *oldSectionItems = self.sectionItems;
  NSArray *oldSectionTitles = self.sectionTitles;

  // Update your backing arrays to the new content
  self.sectionItems = ...
  self.sectionTitles = ...

  // And do the transition
  [SGListAnimator transitionTableView:self.tableView
      fromSectionItems:oldSectionItems
      titles:oldSectionTitles
      toSectionItems:self.sectionItems
      titles:self.sectionTitles
  ];
}

// Obviously, your table view data source methods will all use
// `self.sectionItems` and `self.sectionTitles`.

如果您没有将列表分成部分,则在底层仍然有一个没有标题的部分。您的代码可能如下所示

- (void)updateTableView {
  // Save a reference to the previous content
  NSArray *oldItems = self.tableViewItems;

  // Update your backing array to the new content
  self.tableViewItems = ...

  // And do the transition
  [SGListAnimator transitionTableView:self.tableView
      fromSectionItems:oldItems
      titles:@[@""]
      toSectionItems:self.tableViewItems
      titles:@[@""]
  ];
}

// Use self.tableViewItems in the data source methods.

要启用 SGListAnimator 的一些功能,如支持移动,您需要使用 SGListAnimator 对象,并保留您的支持数据在动画器的 currentSections 属性中。这允许动画器在需要时在底层将转换分成多个子转换。您将为每个部分创建一个简单的 SGListSection 对象;它将标题和项目数组结合在一个方便的对象中。

以下是一个更完整的示例,所有 3 种类型的移动都已打开

#import "SGListAnimator.h"
#import "SGListSection.h"

...

@property (nonatomic) SGListAnimator *animator;

- (void)viewDidLoad {
  [super viewDidLoad];

  ...

  self.animator = [SGListAnimator new];
  self.animator.tableView = self.tableView;
  self.animator.doSectionMoves = YES;
  self.animator.doIntraSectionMoves = YES;
  self.animator.doInterSectionMoves = YES;
}

- (void)updateTableView {
    // Figure out what your new content is, creating each SGListSection
    // object with code like this:
    ...
    SGListSection *section = [SGListSection sectionWithTitle:... items:...];
    [newSections addObject:section];
    ...

    [self.animator transitionTableViewToSections:newSections];
}

// Use self.animator.currentSections in the data source methods, like:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  return self.animator.currentSections[section].items.count;
}

...

功能

  • 处理部分的删除、插入和移动。处理行/项目删除、插入、移动在部分内的移动,以及从一个部分到另一个部分的移动。
  • 同时支持 UITableViewUICollectionView
  • 允许您选择支持三种类型的移动。移动需要更复杂的计算,可能成本更高,这取决于您的列表大小和列表上的变动程度。许多应用不一定需要所有类型的移动。
  • 已彻底进行单元测试和压力测试。包含的演示应用使用随机数据进行了压力测试,这确实在开发期间发现了一个错误。
  • 支持Swift和Objective-C。我们在这里对Swift的利用率不高,如果您有什么改进建议,请告诉我们。演示应用中的表格视图是用Swift编写的,而集合视图是用Objective-C编写的。
  • 在iOS 8和9上进行了测试。我认为应该能在iOS 7上也正常运行。
  • 没有依赖项。

待办事项

  • 动画器尚未在大量列表(即数千个项目)上进行大量测试/分析。使用分页等方式避免向客户端推送大量对象是一个好的方法,因此希望大多数人每次最多只处理一百或两百个项目。
  • 为客户端代码提供一个钩子,以便微调使用的动画会很好。目前对于表格视图,一些合理的选项是硬编码的,而集合视图使用默认行为。