UITableView
reloadData
dynamic cell heights
jumpy scrolling
iOS development

reloadData of UITableView with Dynamic cell heights causes jumpy scrolling

Interview Questions practice on Codemia

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

Browse interview questions

Introduction

When UITableView uses dynamic cell heights, calling reloadData can cause visible jumpiness and scroll position shifts. This happens because the table recalculates layout and estimated heights in bulk. Smoother behavior comes from targeted updates and stable height estimation.

Why reloadData Causes Jumps

reloadData discards visible cell layout and asks the table to recompute all rows. With automatic dimensions, each pass can produce slightly different height estimates until constraints settle. The resulting offset changes feel like jitter during scrolling.

For frequently changing content, avoid full reload when possible.

Use Targeted Row Updates Instead of Full Reload

If only some rows changed, update those rows directly.

swift
1import UIKit
2
3final class FeedViewController: UITableViewController {
4    var items: [String] = []
5
6    override func viewDidLoad() {
7        super.viewDidLoad()
8        tableView.rowHeight = UITableView.automaticDimension
9        tableView.estimatedRowHeight = 100
10    }
11
12    func updateRow(at index: Int, with value: String) {
13        items[index] = value
14        let idx = IndexPath(row: index, section: 0)
15        tableView.reloadRows(at: [idx], with: .none)
16    }
17}

Targeted updates reduce layout disruption and preserve user context.

Animate Height Recalculation Safely

When text content changes and you need recalculated heights, wrap updates with beginUpdates and endUpdates.

swift
1func refreshVisibleLayout() {
2    UIView.performWithoutAnimation {
3        tableView.beginUpdates()
4        tableView.endUpdates()
5    }
6}

This allows height refresh without heavy reload side effects.

Preserve Scroll Offset for Full Refresh Cases

If a full data refresh is unavoidable, capture and restore content offset after updates.

swift
1func reloadPreservingOffset() {
2    let offset = tableView.contentOffset
3    tableView.reloadData()
4    tableView.layoutIfNeeded()
5    tableView.setContentOffset(offset, animated: false)
6}

This does not solve all layout changes, but it reduces visible jump in many scenarios.

Constraint and Estimation Best Practices

Use complete auto layout constraints in cells and set realistic estimatedRowHeight. Large mismatch between estimate and actual height amplifies jumpiness. Also avoid expensive synchronous work in cellForRowAt, because delayed rendering can worsen perceived instability.

If your feed is highly dynamic, consider precomputing text layout off the main update path.

Use Diffable Data Source for Better Update Semantics

Modern table implementations are often smoother when updates are expressed as data snapshots instead of full reload calls. Diffable data source can animate inserts and updates with fewer layout shocks.

swift
1import UIKit
2
3enum Section {
4    case main
5}
6
7final class DiffableController: UITableViewController {
8    var dataSource: UITableViewDiffableDataSource<Section, String>!
9
10    override func viewDidLoad() {
11        super.viewDidLoad()
12        dataSource = UITableViewDiffableDataSource(tableView: tableView) { table, indexPath, item in
13            let cell = table.dequeueReusableCell(withIdentifier: "Cell") ?? UITableViewCell(style: .default, reuseIdentifier: "Cell")
14            cell.textLabel?.text = item
15            cell.textLabel?.numberOfLines = 0
16            return cell
17        }
18    }
19}

Snapshot-based updates can greatly reduce jumpy behavior in dynamic feeds.

Update Scheduling and Main Thread Discipline

Frequent model updates from network callbacks can trigger repeated table recalculation. Coalesce updates and apply them on the main thread at controlled intervals.

swift
1private var pendingReload = false
2
3func scheduleRefresh() {
4    guard !pendingReload else { return }
5    pendingReload = true
6
7    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
8        self.pendingReload = false
9        self.tableView.beginUpdates()
10        self.tableView.endUpdates()
11    }
12}

Coalescing avoids bursty layout work that causes perceived jumpiness.

Cell Configuration Stability

Avoid changing constraint activation patterns on every bind unless necessary. Repeated constraint churn can lead to inconsistent measured heights between passes. Stable constraints and deterministic text configuration make dynamic height behavior much smoother.

This is especially important for image loading cells where asynchronous updates can trigger repeated relayout cycles.

Stable update cadence improves perceived smoothness for users during long scrolling sessions.

Common Pitfalls

  • Calling reloadData on every small model change.
  • Leaving incomplete cell constraints and causing unstable height calculation.
  • Using unrealistic estimated heights that differ greatly from real content.
  • Performing expensive work during cell binding and increasing frame drops.

Summary

  • Prefer row-level reloads over full reloadData for dynamic tables.
  • Use beginUpdates and endUpdates for smooth height recalculation.
  • Preserve offset when full refresh is unavoidable.
  • Keep constraints complete and estimated heights realistic.

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.