iOS
UICollectionView
animation
reloadItemsAtIndexPaths
Swift

Avoid animation of UICollectionView after reloadItemsAtIndexPaths

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When you call reloadItems(at:) on a UICollectionView, UIKit performs a fade animation by default. This is often unwanted — if you are updating cell content (like a counter or image), the fade looks jarring. The fix is to wrap the reload in UIView.performWithoutAnimation or use performBatchUpdates with animations disabled. Each approach disables the implicit animation that UIKit applies to collection view updates.

The Problem

swift
// This triggers a fade animation on the reloaded cells
collectionView.reloadItems(at: [IndexPath(item: 3, section: 0)])

UIKit wraps reloadItems(at:) in a CATransaction with a default fade animation. Even though you did not request an animation, the cells briefly fade out and back in.

swift
UIView.performWithoutAnimation {
    collectionView.reloadItems(at: [IndexPath(item: 3, section: 0)])
}

This wraps the reload in a CATransaction that disables all animations for its duration. The cells update instantly with no visual transition.

For multiple index paths:

swift
1let pathsToReload = [
2    IndexPath(item: 0, section: 0),
3    IndexPath(item: 5, section: 1),
4    IndexPath(item: 2, section: 2)
5]
6
7UIView.performWithoutAnimation {
8    collectionView.reloadItems(at: pathsToReload)
9}

Fix 2: performBatchUpdates with Animations Disabled

swift
1UIView.setAnimationsEnabled(false)
2collectionView.performBatchUpdates({
3    collectionView.reloadItems(at: [IndexPath(item: 3, section: 0)])
4}, completion: { _ in
5    UIView.setAnimationsEnabled(true)
6})

This globally disables animations during the batch update. Re-enable animations in the completion block to avoid affecting other UI elements.

Fix 3: CATransaction

swift
1CATransaction.begin()
2CATransaction.setDisableActions(true)
3
4collectionView.reloadItems(at: [IndexPath(item: 3, section: 0)])
5
6CATransaction.commit()

setDisableActions(true) suppresses all implicit Core Animation actions (including the fade) within the transaction.

Fix 4: Reconfigure Cells (iOS 15+)

Starting with iOS 15, reconfigureItems(at:) updates cell content without replacing the cell, which means no animation:

swift
// iOS 15+ only
collectionView.reconfigureItems(at: [IndexPath(item: 3, section: 0)])

This calls your cell configuration closure again for the specified items without creating new cells or triggering any animation. It is the cleanest solution if you target iOS 15 or later.

Fix 5: Update Cell Directly

Skip reloadItems entirely and update the cell in place:

swift
if let cell = collectionView.cellForItem(at: IndexPath(item: 3, section: 0)) as? MyCell {
    cell.configure(with: updatedData)
}

This modifies the existing cell without involving the collection view's update mechanism. No animation occurs because no reload happens.

Reloading Entire Sections Without Animation

swift
UIView.performWithoutAnimation {
    collectionView.reloadSections(IndexSet(integer: 0))
}

The same approach works for reloadSections(_:) and reloadData():

swift
1// reloadData doesn't animate by default, but if embedded in an animated context:
2UIView.performWithoutAnimation {
3    collectionView.reloadData()
4}

Note that reloadData() does not normally animate, but can inherit animations from a surrounding animation block.

UITableView Equivalent

The same techniques work for UITableView:

swift
UIView.performWithoutAnimation {
    tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .none)
}

For UITableView, you can also pass .none as the animation parameter:

swift
tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .none)

UICollectionView does not have this animation parameter — which is why the wrapper approaches are necessary.

Common Pitfalls

  • Forgetting to re-enable animations: When using UIView.setAnimationsEnabled(false), always re-enable in the completion block. Leaving animations disabled globally breaks all subsequent UI animations.
  • Using reloadData() when you mean reloadItems(at:): reloadData() replaces all cells and resets scroll position. Use reloadItems(at:) for targeted updates.
  • Calling cellForItem(at:) on off-screen cells: If the cell is not visible, cellForItem(at:) returns nil. Only use the direct-update approach for visible cells.
  • Mixing animated and non-animated updates: If you perform animated inserts/deletes in the same batch as non-animated reloads, the animation disabling affects everything. Separate them into different batch updates.
  • iOS version compatibility: reconfigureItems(at:) requires iOS 15+. Check availability before using it: if #available(iOS 15.0, *) { ... }.

Summary

  • reloadItems(at:) triggers a default fade animation on UICollectionView
  • Wrap in UIView.performWithoutAnimation { } to suppress the animation
  • Use CATransaction.setDisableActions(true) as an alternative
  • On iOS 15+, use reconfigureItems(at:) which never animates
  • Update cells directly via cellForItem(at:) to bypass the reload mechanism entirely
  • UITableView has a .none animation parameter but UICollectionView does not

Course illustration
Course illustration

All Rights Reserved.