UICollectionView
iOS Development
Animation
Swift
Data Handling

UICollectionView animate data change

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Animating data changes in a UICollectionView is less about visual polish and more about keeping the UI and data source perfectly synchronized. If your backing array and collection-view updates drift apart, you get “invalid update” crashes instead of smooth insertions, deletions, and moves.

The Basic Rule: Update Data and View Together

A collection view does not store your items for you. It asks the data source how many items exist and which cell belongs at each index path. Because of that, animation code must reflect the same change in both places.

Suppose you have a simple list of strings:

swift
1final class NamesViewController: UIViewController, UICollectionViewDataSource {
2    @IBOutlet private weak var collectionView: UICollectionView!
3
4    private var items = ["Ada", "Grace", "Linus"]
5
6    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
7        items.count
8    }
9
10    func collectionView(
11        _ collectionView: UICollectionView,
12        cellForItemAt indexPath: IndexPath
13    ) -> UICollectionViewCell {
14        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
15        return cell
16    }
17}

If you want to insert a new item at the beginning, update the array and then tell the collection view about the matching insertion.

Animating Inserts, Deletes, and Moves

The standard API is performBatchUpdates.

swift
1func insertItem() {
2    items.insert("Margaret", at: 0)
3
4    collectionView.performBatchUpdates {
5        collectionView.insertItems(at: [IndexPath(item: 0, section: 0)])
6    }
7}

Deleting works the same way:

swift
1func deleteItem(at index: Int) {
2    items.remove(at: index)
3
4    collectionView.performBatchUpdates {
5        collectionView.deleteItems(at: [IndexPath(item: index, section: 0)])
6    }
7}

And moving an item requires both the array mutation and the collection-view move:

swift
1func moveItem(from source: Int, to destination: Int) {
2    let moved = items.remove(at: source)
3    items.insert(moved, at: destination)
4
5    collectionView.performBatchUpdates {
6        collectionView.moveItem(
7            at: IndexPath(item: source, section: 0),
8            to: IndexPath(item: destination, section: 0)
9        )
10    }
11}

The animation comes from the collection view understanding exactly what changed.

When to Use reloadItems and When Not To

If the number of items stays the same and only the contents of one cell change, use reloadItems(at:) instead of deleting and reinserting.

swift
1func renameFirstItem() {
2    items[0] = "Katherine"
3
4    collectionView.performBatchUpdates {
5        collectionView.reloadItems(at: [IndexPath(item: 0, section: 0)])
6    }
7}

This keeps the structure stable and only refreshes the affected cell.

What you should avoid for small changes is reloadData(). It refreshes everything and skips the fine-grained animation you usually want.

Diffable Data Source Is Usually Simpler

For modern UIKit code, UICollectionViewDiffableDataSource is often the cleanest solution. Instead of manually calling insert, delete, and move APIs, you build a snapshot and apply it. UIKit computes the differences and animates them for you.

swift
1import UIKit
2
3enum Section {
4    case main
5}
6
7final class DiffableExampleViewController: UIViewController {
8    @IBOutlet private weak var collectionView: UICollectionView!
9
10    private var dataSource: UICollectionViewDiffableDataSource<Section, String>!
11
12    override func viewDidLoad() {
13        super.viewDidLoad()
14
15        dataSource = UICollectionViewDiffableDataSource<Section, String>(
16            collectionView: collectionView
17        ) { collectionView, indexPath, itemIdentifier in
18            collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
19        }
20
21        var snapshot = NSDiffableDataSourceSnapshot<Section, String>()
22        snapshot.appendSections([.main])
23        snapshot.appendItems(["Ada", "Grace", "Linus"])
24        dataSource.apply(snapshot, animatingDifferences: false)
25    }
26
27    func applyUpdatedData() {
28        var snapshot = NSDiffableDataSourceSnapshot<Section, String>()
29        snapshot.appendSections([.main])
30        snapshot.appendItems(["Margaret", "Ada", "Grace", "Linus"])
31        dataSource.apply(snapshot, animatingDifferences: true)
32    }
33}

This approach removes a lot of manual bookkeeping and is especially useful when multiple inserts and deletes happen at once.

Choosing Between the Two Approaches

Manual performBatchUpdates works well when:

  • you already manage a small local array
  • changes are simple and explicit
  • you want direct control over the exact operations

Diffable data source works well when:

  • the screen has more complex update logic
  • items move often
  • you want UIKit to compute the differences safely

Both are valid. The main requirement is consistency between your model state and the visual updates.

Common Pitfalls

The most common crash is updating the collection view without updating the data source, or updating them in a mismatched way. If the view thinks an item was inserted but numberOfItemsInSection still returns the old count, UIKit will throw an invalid update exception.

Another mistake is using reloadData() after a carefully planned batch update. That often cancels the point of the animation and can make the UI feel jumpy.

Layout changes can also affect animations. If your cell sizes depend on dynamic content, make sure the layout invalidation behavior matches the update pattern, or cells may animate awkwardly.

Finally, run UI updates on the main thread. Collection view mutations from background work can cause subtle crashes and inconsistent animation timing.

Summary

  • 'UICollectionView animations work only when the data source and UI updates stay in sync.'
  • Use performBatchUpdates for inserts, deletes, moves, and targeted reloads.
  • Update the backing data to match the exact collection-view operation.
  • Prefer diffable data source for more complex or frequent state changes.
  • Avoid reloadData() when you want smooth, fine-grained animations.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.