UITableView
iOS Development
Swift Programming
Mobile App Design
Section Headers

UITableView hide header from empty section

Interview Questions practice on Codemia

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

Browse interview questions

Introduction

Hiding a UITableView header for an empty section is slightly trickier than returning an empty string. UIKit can still reserve header height or display spacing unless the title, custom view, and height logic all agree that the section should be hidden.

Keep Section Visibility in One Rule

The safest pattern is to define one helper that decides whether a section should be visible:

swift
func hasRows(in section: Int) -> Bool {
    return !data[section].isEmpty
}

Once that rule exists, reuse it everywhere header behavior is decided. Without a shared rule, it is easy to hide the title but still leave a visible blank gap because the height method returns a nonzero value.

Return No Header Content for Empty Sections

If you use the default text header, return nil when the section has no rows:

swift
1import UIKit
2
3final class InventoryViewController: UITableViewController {
4    var data: [[String]] = [
5        ["Apples", "Bananas"],
6        [],
7        ["Bread"]
8    ]
9
10    let sectionTitles = ["Fruit", "Empty", "Bakery"]
11
12    override func numberOfSections(in tableView: UITableView) -> Int {
13        return data.count
14    }
15
16    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
17        return data[section].count
18    }
19
20    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
21        return hasRows(in: section) ? sectionTitles[section] : nil
22    }
23
24    private func hasRows(in section: Int) -> Bool {
25        return !data[section].isEmpty
26    }
27}

That removes the text, but it is not always enough to remove the space. Many table styles still reserve a default height unless you handle that separately.

Return a Minimal Height for Empty Sections

To fully hide the header, override the height delegate and return .leastNormalMagnitude for empty sections:

swift
1import UIKit
2
3override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
4    return hasRows(in: section) ? 32 : .leastNormalMagnitude
5}

Using 0 does not always behave as expected. In grouped or inset-grouped tables, UIKit may still apply default spacing. .leastNormalMagnitude is the common workaround when you want the header effectively removed.

Match Custom Header Views to the Same Rule

If you provide a custom header view, it must follow the same condition:

swift
1import UIKit
2
3override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
4    guard hasRows(in: section) else {
5        return nil
6    }
7
8    let label = UILabel()
9    label.text = sectionTitles[section]
10    label.font = .preferredFont(forTextStyle: .headline)
11    label.textColor = .secondaryLabel
12
13    let container = UIView()
14    container.backgroundColor = .systemBackground
15    label.translatesAutoresizingMaskIntoConstraints = false
16    container.addSubview(label)
17
18    NSLayoutConstraint.activate([
19        label.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 16),
20        label.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: -6)
21    ])
22
23    return container
24}

If viewForHeaderInSection creates a view while heightForHeaderInSection tries to hide it, you end up with inconsistent results and harder-to-debug layout artifacts.

Reload Sections When Data Changes

Header visibility often breaks after dynamic updates because the table view still uses stale layout state. When rows change, reload the affected section:

swift
1import UIKit
2
3func updateSection(_ section: Int, with newRows: [String]) {
4    guard section < data.count else { return }
5    data[section] = newRows
6
7    tableView.performBatchUpdates({
8        tableView.reloadSections(IndexSet(integer: section), with: .automatic)
9    })
10}

That keeps the row count and header state synchronized. If you update the model but do not reload the section, the table may continue drawing the old header configuration.

Consider Filtering Empty Sections Out

If empty sections never need to appear in the interface, a cleaner approach is to remove them before presenting the data:

swift
1struct SectionModel {
2    let title: String
3    let rows: [String]
4}
5
6let visibleSections = sections.filter { !$0.rows.isEmpty }

This simplifies the delegate methods because the table view no longer needs special empty-section behavior. The tradeoff is that the visible section indices may no longer match the original data source indices.

Common Pitfalls

  • Returning nil for the header title but forgetting to reduce the header height for empty sections.
  • Returning 0 height and expecting all table styles to remove spacing completely.
  • Using one condition in titleForHeaderInSection and a different condition in viewForHeaderInSection.
  • Updating the data model without reloading the affected section.
  • Keeping empty sections in the table when the simpler design would be to filter them out.

Summary

  • Hide empty-section headers by coordinating title, custom view, and height logic.
  • Use one shared helper so every header decision follows the same visibility rule.
  • Return .leastNormalMagnitude rather than 0 when you want empty headers truly hidden.
  • Reload sections after data changes so header state stays in sync.
  • If the UX allows it, remove empty sections from the data before rendering.

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.