← All topics/Performance & optimization

Question 6

Table and collection view performance

A table or collection view gets sluggish as content grows. What mistakes do you look for, and how do you fix them?

Follow-ups

  • Reuse, Auto Layout, prefetch, diffable tradeoffs?

Answer outline

Sluggish lists almost always mean too much work per cell while it scrolls into view. Check these areas in order:

  1. 1.Reuse: prepareForReuse must reset every piece of state, and async loads must cancel and re-check the item identifier before applying a result. Wrong reuse shows up as stutter and the wrong content in cells.
  2. 2.Layout: too many constraints per cell, frequent intrinsic content size changes, or self-sizing without cached heights means repeated layout passes. Set an estimated row height and keep a complete, unambiguous Auto Layout chain from top to bottom for self-sizing cells.
  3. 3.Prefetching: UITableViewDataSourcePrefetching and UICollectionViewDataSourcePrefetching let you start loads before rows appear. Cancel them in cancelPrefetchingForRowsAt or cancelPrefetchingForItemsAt when the user scrolls away.
  4. 4.Diffable data source: needs stable identifiers so an item is recognized as the same one across snapshots. You get animated updates with far fewer performBatchUpdates crashes, but the model must be diff-friendly and every snapshot must stay consistent.

Start by reading what cellForRowAt does on every call. Date formatting, attributed string building, and disk reads inside it are the usual offenders. All of them can be precomputed into the model before the cell asks for them.

Principles

  • Keep cellForRowAt and cellForItemAt cheap, and move work to willDisplay or background tasks with a clear lifecycle.
  • Profile scrolling with Animation Hitches and Time Profiler together, so you see dropped frames and their cause side by side.
  • Deep Auto Layout hierarchies cost a full pass per cell, so flatten them or lay out hot cells manually.
  • Give every item a stable identifier, because diffable snapshots and SwiftUI ForEach both depend on it.

Give the table an estimate so it can size cells lazily instead of measuring every row up front:

UIKit: estimated row height
tableView.estimatedRowHeight = 120
tableView.rowHeight = UITableView.automaticDimension

Follow-up angles

  • SwiftUI List has the same identity rules. Unstable ForEach identifiers hurt scroll performance just as much as bad reuse does in UIKit.
  • Cells with opaque backgrounds and fewer translucent overlays give the compositor less blending work on every frame.