iPhone
UITableView
scrolling performance
iOS development
mobile optimization

Tricks for improving iPhone UITableView scrolling performance?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

UITableView can render large datasets smoothly, but only when cell work is lightweight and predictable. Scrolling problems usually come from synchronous image work, expensive layout, or excessive reload operations. This guide focuses on practical performance improvements you can apply immediately.

Reuse Cells Correctly

Reuse is foundational. Register once and always dequeue in cellForRowAt.

swift
1tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
2
3func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
4    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
5    cell.textLabel?.text = items[indexPath.row]
6    return cell
7}

Do not allocate new cells manually in hot paths unless you have a specific reason.

Keep cellForRowAt Fast

Avoid expensive work during scrolling.

  • No network calls in cellForRowAt.
  • No heavy JSON parsing in cellForRowAt.
  • Minimize formatter creation and expensive string computation.

Precompute display models in the data layer, then bind prebuilt values in cells.

Optimize Image Loading

Image decoding on the main thread causes visible stutter. Use async loading, caching, and cancellation for reused cells.

swift
1final class AvatarCell: UITableViewCell {
2    private var task: URLSessionDataTask?
3
4    override func prepareForReuse() {
5        super.prepareForReuse()
6        task?.cancel()
7        imageView?.image = UIImage(systemName: "person.circle")
8    }
9
10    func configure(url: URL) {
11        task = URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
12            guard let data, let image = UIImage(data: data) else { return }
13            DispatchQueue.main.async {
14                self?.imageView?.image = image
15                self?.setNeedsLayout()
16            }
17        }
18        task?.resume()
19    }
20}

For production apps, use an image pipeline library with memory and disk cache.

Use Estimated Heights and Simple Auto Layout

Self-sizing cells are useful, but deeply nested constraints can hurt scrolling performance.

swift
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 72

Keep constraints minimal and avoid repeatedly changing constraint trees on reuse.

Prefetch Data Before It Is Needed

UITableViewDataSourcePrefetching lets you load content before rows appear.

swift
1class ViewController: UIViewController, UITableViewDataSourcePrefetching {
2    func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
3        let ids = indexPaths.map { items[$0.row].id }
4        dataLoader.prefetch(ids: ids)
5    }
6}

Prefetching reduces waiting time and removes burst work during fast scroll gestures.

Batch Updates and Diffing

Reloading the entire table repeatedly is expensive. Prefer targeted updates.

swift
tableView.performBatchUpdates {
    tableView.insertRows(at: [IndexPath(row: newIndex, section: 0)], with: .automatic)
}

For complex feeds, use diffable data sources to apply minimal changes.

Measure Before and After

Use Instruments to confirm improvements.

  • Time Profiler for CPU hotspots.
  • Core Animation for frame drops.
  • Allocations for memory churn from cell reuse issues.

Performance work should be data-driven, not guess-driven.

Reduce Layout and Drawing Cost in Cells

Custom drawing, shadows, and deep view hierarchies increase per-frame work. Flatten view trees where possible and avoid expensive layer effects in scrolling cells.

swift
cell.layer.shadowOpacity = 0 // avoid dynamic shadows in fast lists
cell.contentView.layer.cornerRadius = 8
cell.contentView.layer.masksToBounds = true

If you need complex visual effects, pre-render assets or apply effects only when the cell is stationary.

Cache Expensive Formatters and Derived Text

Date and number formatters are expensive to recreate repeatedly. Initialize once and reuse.

swift
1final class Formatters {
2    static let shared = Formatters()
3    let dateFormatter: DateFormatter = {
4        let f = DateFormatter()
5        f.dateStyle = .medium
6        f.timeStyle = .short
7        return f
8    }()
9}

Then bind preformatted text from view models so cell rendering remains cheap.

Main-Thread Hygiene

Use DispatchQueue.main.async only for UI assignment, not processing.

  • Decode and resize images off the main thread.
  • Parse payloads before data hits table view callbacks.
  • Coalesce updates to avoid frequent relayout bursts.

This discipline prevents dropped frames during rapid scrolling gestures.

Common Pitfalls

  • Doing heavy computation inside cellForRowAt.
  • Loading and decoding images on the main thread.
  • Calling reloadData for small incremental changes.
  • Overcomplicated Auto Layout hierarchies inside cells.
  • Skipping instrumentation and optimizing the wrong code path.

Summary

  • Fast scrolling depends on lightweight cells and predictable reuse.
  • Move heavy work out of rendering callbacks.
  • Use async image loading with cancellation and cache.
  • Apply incremental updates instead of full reloads.
  • Validate gains with Instruments on real data and devices.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

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

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.