UITableView
tableHeaderView
autolayout
iOS development
Swift programming

How do I set the height of tableHeaderView UITableView with autolayout?

Interview Questions practice on Codemia

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

Browse interview questions

Introduction

tableHeaderView is a little awkward with Auto Layout because UITableView does not automatically resize the header based on its constraints. The usual fix is to let Auto Layout compute the header's fitting size, update the header frame manually, and then assign it back to tableView.tableHeaderView.

Why the Header Does Not Resize Automatically

The cells in a table view can participate in self-sizing much more naturally than the table header. tableHeaderView is just a plain UIView attached above the first section, and the table view mainly respects its frame, not its internal constraints.

That means two things must both be true:

  • the header's internal subviews need complete constraints,
  • you still need to convert that Auto Layout result into a concrete frame height.

If you only add constraints and never update the frame, the header often appears with the wrong height.

The Standard Pattern

Create the header view, configure its constraints, force layout, measure the fitting height, update the frame, and then reassign it.

swift
1import UIKit
2
3final class HeaderTableViewController: UITableViewController {
4    private let titleLabel = UILabel()
5    private let headerView = UIView()
6
7    override func viewDidLoad() {
8        super.viewDidLoad()
9
10        titleLabel.numberOfLines = 0
11        titleLabel.text = "A long header title that should wrap onto multiple lines."
12
13        headerView.addSubview(titleLabel)
14        titleLabel.translatesAutoresizingMaskIntoConstraints = false
15
16        NSLayoutConstraint.activate([
17            titleLabel.topAnchor.constraint(equalTo: headerView.topAnchor, constant: 16),
18            titleLabel.leadingAnchor.constraint(equalTo: headerView.leadingAnchor, constant: 16),
19            titleLabel.trailingAnchor.constraint(equalTo: headerView.trailingAnchor, constant: -16),
20            titleLabel.bottomAnchor.constraint(equalTo: headerView.bottomAnchor, constant: -16),
21        ])
22
23        tableView.tableHeaderView = headerView
24        updateHeaderHeight()
25    }
26
27    override func viewDidLayoutSubviews() {
28        super.viewDidLayoutSubviews()
29        updateHeaderHeight()
30    }
31
32    private func updateHeaderHeight() {
33        guard let header = tableView.tableHeaderView else { return }
34
35        let targetSize = CGSize(
36            width: tableView.bounds.width,
37            height: UIView.layoutFittingCompressedSize.height
38        )
39
40        let newSize = header.systemLayoutSizeFitting(
41            targetSize,
42            withHorizontalFittingPriority: .required,
43            verticalFittingPriority: .fittingSizeLevel
44        )
45
46        if header.frame.height != newSize.height {
47            header.frame.size.height = newSize.height
48            tableView.tableHeaderView = header
49        }
50    }
51}

Two details matter a lot here:

  • 'systemLayoutSizeFitting asks Auto Layout for the correct compressed height.'
  • reassigning tableView.tableHeaderView tells the table view to respect the updated frame.

Why viewDidLayoutSubviews Is Useful

The header width often depends on the table view's final bounds. During viewDidLoad, those bounds may not yet be final, especially during rotation or initial layout.

Calling the size update in viewDidLayoutSubviews ensures the header is measured using the actual table width. The if header.frame.height != newSize.height guard prevents endless reassignment loops.

Constraint Requirements Inside the Header

Auto Layout can only compute a height if the header has enough information. Typical mistakes include:

  • missing bottom constraints,
  • subviews with ambiguous widths,
  • labels that need numberOfLines = 0 but were left at one line,
  • content that depends on a width not yet assigned.

If the internal constraints are incomplete, systemLayoutSizeFitting returns an unhelpful value.

Dynamic Content Updates

If the header content changes after the table first appears, call the same sizing method again.

swift
1func refreshHeader(text: String) {
2    titleLabel.text = text
3    updateHeaderHeight()
4}

This is common when the header shows fetched text, localization changes, or an expandable summary.

Common Pitfalls

The most common mistake is expecting tableHeaderView to self-size the way modern table-view cells do. It does not. You must update the frame manually.

Another issue is forgetting to assign the header back to tableView.tableHeaderView after changing its height. Updating the frame alone is often not enough to trigger the table view to honor the new size.

Developers also sometimes calculate the fitting height before the table view has its final width, which produces the wrong result for wrapping labels.

Summary

  • 'tableHeaderView uses its frame height, not just Auto Layout constraints.'
  • Build correct internal constraints first so the header has a measurable size.
  • Use systemLayoutSizeFitting to compute the height.
  • Reassign tableView.tableHeaderView after updating the frame.
  • Recalculate in viewDidLayoutSubviews and after dynamic content changes.

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.