UITableView
viewForHeaderInSection
reloadData
iOS development
Swift

UITableView viewForHeaderInSection not called during reloadData

Interview Questions practice on Codemia

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

Browse interview questions

Introduction

If tableView(_:viewForHeaderInSection:) is not called after reloadData(), that usually does not mean the table view is broken. UITableView creates header views on demand, reuses them, and may decide that a visible header does not need to be recreated just because the data source was reloaded.

The key point is that reloadData() refreshes the model and row structure, not the exact sequence of delegate calls you might expect. If you need a particular header to update immediately, target that section more explicitly.

Why the Delegate Method May Not Fire

UITableView asks for headers only when it needs them. Common reasons the method is not called include:

  • the section is off-screen
  • the header height is zero or effectively hidden
  • the table already has a reusable header view it can keep using
  • the section is using a title-based header rather than a custom view

This is similar to cell reuse. Logging inside viewForHeaderInSection can be misleading because a missing log line does not always mean the UI failed to update.

Make Sure the Header Is Actually Configured to Exist

A custom section header needs a visible height and a registered reuse identifier if you are using UITableViewHeaderFooterView.

swift
1import UIKit
2
3final class HeaderView: UITableViewHeaderFooterView {
4    static let reuseIdentifier = "HeaderView"
5
6    func configure(title: String) {
7        textLabel?.text = title
8    }
9}
10
11final class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
12    @IBOutlet private weak var tableView: UITableView!
13    private var titles = ["Open", "Closed"]
14
15    override func viewDidLoad() {
16        super.viewDidLoad()
17        tableView.register(
18            HeaderView.self,
19            forHeaderFooterViewReuseIdentifier: HeaderView.reuseIdentifier
20        )
21    }
22
23    func numberOfSections(in tableView: UITableView) -> Int {
24        titles.count
25    }
26
27    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
28        3
29    }
30
31    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
32        44
33    }
34
35    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
36        let header = tableView.dequeueReusableHeaderFooterView(
37            withIdentifier: HeaderView.reuseIdentifier
38        ) as! HeaderView
39        header.configure(title: titles[section])
40        return header
41    }
42}

If the header height is effectively zero, UIKit has no reason to ask for the view.

Use reloadSections When Only Headers Changed

If the section data did not fundamentally change but the header text or appearance did, reloadSections is often better than reloadData().

swift
1func updateHeaderTitle(_ title: String, in section: Int) {
2    titles[section] = title
3    tableView.reloadSections(IndexSet(integer: section), with: .none)
4}

That tells the table view exactly which section needs to be reconsidered. It is more precise and usually easier to reason about than reloading everything.

Update the Visible Header Directly When Needed

If the header is already on screen, you can also update it directly.

swift
if let header = tableView.headerView(forSection: 0) as? HeaderView {
    header.configure(title: "Updated")
}

This is useful when you want the currently visible header to change immediately without waiting for reuse or for the section to scroll off and back on.

reloadData() Does Not Promise Full View Recreation

This is the source of most confusion. reloadData() invalidates the table view's current data and causes it to ask the data source for what it needs to draw again. It does not promise that every existing row and section header will be thrown away and rebuilt from scratch in a predictable order.

That means reloadData() is a data refresh tool, not a delegate-call trigger.

If you changed header height constraints or auto layout conditions, beginUpdates() and endUpdates() can also help the table recalculate layout.

Common Pitfalls

A common mistake is expecting viewForHeaderInSection to be called for off-screen sections immediately after reloadData(). Another is returning zero height, which prevents the header from existing in the first place. Developers also sometimes call reloadData() for a tiny header-only change when reloadSections or direct header updates are a better fit. Finally, mixing titleForHeaderInSection and viewForHeaderInSection without being clear about which header style is active can make the behavior look inconsistent.

Summary

  • 'reloadData() does not guarantee a fresh call to viewForHeaderInSection for every section.'
  • Header views are created on demand and reused.
  • Make sure the section header has a visible height and proper registration.
  • Use reloadSections when a specific header needs to refresh.
  • Update the visible header directly if you need an immediate on-screen change.

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.