更改数据源时 UICollectionView 中的平滑动画

Posted

技术标签:

【中文标题】更改数据源时 UICollectionView 中的平滑动画【英文标题】:Smooth animation in UICollectionView when changing data source 【发布时间】:2019-04-03 20:14:26 【问题描述】:

我有一个 UISegmentControl,用于切换 UICollectionView 的数据源。数据源是不同类型的对象。

例如,对象可能看起来像这样

struct Student 
    let name: String
    let year: String
    ...

struct Teacher 
    let name: String
    let department: String
    ...

而在包含 CollectionView 的视图中,会有这样的代码:

var students = [Student]()
var teachers = [Teachers]()
... // populate these with data via an API

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    if(segmentControl.titleForSegment(at: segmentControl.selectedSegmentIndex) == "Students") 
        return students?.count ?? 0
     else 
        return teachers?.count ?? 0
     


func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "personCell", for: indexPath) as! PersonCell
    if(segmentControl.titleForSegment(at: segmentControl.selectedSegmentIndex)! == "Students") 
        cell.title = students[indexPath.row].name
        cell.subtitle = students[indexPath.row].year
     else 
        cell.title = teachers[indexPath.row].name
        cell.subtitle = teachers[indexPath.row].subject
    
    return cell


@IBAction func segmentChanged(_ sender: AnyObject) 
    collectionView.reloadData()

这会在两个数据源之间正确切换,但不会为更改设置动画。我试过这个:

self.collectionView.performBatchUpdates(
    let indexSet = IndexSet(integersIn: 0...0)
    self.collectionView.reloadSections(indexSet)
, completion: nil)

但这只是崩溃(我认为这是因为 performBatchUpdates 对删除什么和添加什么感到困惑)。

是否有任何简单的方法可以使这项工作顺利进行,而无需在 collectionView 中存储当前项目的单独数组,或者这是使这项工作顺利进行的唯一方法?

非常感谢!

【问题讨论】:

【参考方案1】:

如果您的 Cell 的 UI 在不同的数据源中看起来相同,您可以在数据源上抽象一个 ViewModel,如下所示:

struct CellViewModel 
    let title: String
    let subTitle: String
    ...

然后每次从 API 获取数据时,动态生成 ViewModel

    var students = [Student]()
    var teachers = [Teachers]()
    ... // populate these with data via an API

    var viewModel = [CellViewModel]()
    ... // populate it from data above by checking currently selected segmentBarItem

    if(segmentControl.titleForSegment(at: segmentControl.selectedSegmentIndex)! == "Students") 
        viewModel = generateViewModelFrom(students)
     else 
        viewModel = generateViewModelFrom(teachers)
    

因此,您始终在 UICollectionView 中保留一个数据源数组。

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    return viewModel?.count ?? 0


func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "personCell", for: indexPath) as! PersonCell
    cell.title = viewModel[indexPath.row].title
    cell.subtitle = viewModel[indexPath.row].subTitle
    return cell


@IBAction func segmentChanged(_ sender: AnyObject) 
    collectionView.reloadData()

然后试试你的 performBatchUpdates:

self.collectionView.performBatchUpdates(
    let indexSet = IndexSet(integersIn: 0...0)
    self.collectionView.reloadSections(indexSet)
, completion: nil)

【讨论】:

以上是关于更改数据源时 UICollectionView 中的平滑动画的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionview 在滚动时更改选择/禁用选择 - iOS

UICollectionView DataSource 方法未在 CoreData 更改时调用

设备方向更改时如何更改 UiCollectionView 单元格大小?

当 UICollectionView 更改布局时覆盖 UIAccessibilityLayoutChangedNotification

在方向更改时为 UICollectionView 设置动画

单击 UIButton 时切换/更改 UICollectionView 中的单元格