iOS
UITableView
Font Size
Section Headers
Swift Programming

Changing Font Size For UITableView Section Headers

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Changing section header font size in UITableView looks simple, but the default header API gives very limited typography control. In production apps, teams usually need consistent branding, Dynamic Type support, and readable headers across devices. The most reliable approach is to use a custom header view and drive style through code instead of relying on default title rendering.

Why Default Section Headers Are Limited

When you return text from tableView(_:titleForHeaderInSection:), UIKit builds the header label internally. That internal label is not designed for deep styling, so changing font family, size, and spacing is inconsistent and fragile. You can sometimes alter appearance globally with UITableViewHeaderFooterView.appearance(), but that affects every table and can cause side effects.

For predictable behavior, move to tableView(_:viewForHeaderInSection:) and return your own UITableViewHeaderFooterView. This gives full control over typography, colors, layout margins, and accessibility behavior.

Build a Reusable Header View

A reusable subclass is the cleanest foundation. It centralizes font logic and avoids copy-paste header code in every view controller.

swift
1import UIKit
2
3final class SectionHeaderView: UITableViewHeaderFooterView {
4    static let reuseID = "SectionHeaderView"
5
6    private let titleLabel: UILabel = {
7        let label = UILabel()
8        label.translatesAutoresizingMaskIntoConstraints = false
9        label.numberOfLines = 1
10        label.font = UIFont.preferredFont(forTextStyle: .headline)
11        label.adjustsFontForContentSizeCategory = true
12        label.textColor = .label
13        return label
14    }()
15
16    override init(reuseIdentifier: String?) {
17        super.init(reuseIdentifier: reuseIdentifier)
18        contentView.addSubview(titleLabel)
19
20        NSLayoutConstraint.activate([
21            titleLabel.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
22            titleLabel.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor),
23            titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 6),
24            titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -6)
25        ])
26
27        contentView.backgroundColor = .systemBackground
28    }
29
30    required init?(coder: NSCoder) {
31        fatalError("init(coder:) has not been implemented")
32    }
33
34    func configure(title: String, pointSize: CGFloat, weight: UIFont.Weight = .semibold) {
35        titleLabel.text = title
36        titleLabel.font = .systemFont(ofSize: pointSize, weight: weight)
37    }
38}

This class exposes a configure method so your table controller can vary font size per section when needed. If your style is fixed, you can remove pointSize and keep a single design token.

Connect the Header View in UITableView

Register the header view and return it from the delegate method. This pattern is safe with reuse and keeps scrolling smooth.

swift
1import UIKit
2
3final class SettingsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
4    private let tableView = UITableView(frame: .zero, style: .insetGrouped)
5    private let sectionTitles = ["General", "Notifications", "Privacy"]
6
7    override func viewDidLoad() {
8        super.viewDidLoad()
9        view.backgroundColor = .systemBackground
10
11        tableView.translatesAutoresizingMaskIntoConstraints = false
12        tableView.dataSource = self
13        tableView.delegate = self
14        tableView.register(SectionHeaderView.self,
15                           forHeaderFooterViewReuseIdentifier: SectionHeaderView.reuseID)
16
17        view.addSubview(tableView)
18        NSLayoutConstraint.activate([
19            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
20            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
21            tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
22            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
23        ])
24    }
25
26    func numberOfSections(in tableView: UITableView) -> Int {
27        sectionTitles.count
28    }
29
30    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
31        3
32    }
33
34    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
35        UITableViewCell(style: .default, reuseIdentifier: nil)
36    }
37
38    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
39        guard let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: SectionHeaderView.reuseID)
40                as? SectionHeaderView else {
41            return nil
42        }
43
44        let title = sectionTitles[section]
45        let size: CGFloat = section == 0 ? 22 : 17
46        header.configure(title: title, pointSize: size)
47        return header
48    }
49
50    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
51        44
52    }
53}

With this setup you can easily tune typography for hierarchy, such as a larger first section and standard size for the rest.

Support Dynamic Type and Accessibility

Hardcoding a single size can hurt accessibility. If your design allows scaling, use UIFontMetrics so custom fonts grow with system text settings.

swift
1let baseFont = UIFont.systemFont(ofSize: 18, weight: .semibold)
2let scaled = UIFontMetrics(forTextStyle: .headline).scaledFont(for: baseFont)
3titleLabel.font = scaled
4titleLabel.adjustsFontForContentSizeCategory = true

This preserves design intent while remaining accessible for users with larger text preferences.

Common Pitfalls

Teams often keep titleForHeaderInSection and viewForHeaderInSection at the same time, then wonder why styling seems random. Once you use a custom view, remove default header title logic. Another frequent issue is forgetting to register the header class, which causes dequeue failures and missing headers. Fixed heights can also clip larger fonts, especially under accessibility sizes, so test with larger content categories and increase header height if needed. Finally, global appearance customization can unintentionally affect unrelated tables, so scope styles to reusable header views whenever possible.

Summary

  • Default table section titles are easy, but they do not provide robust font control.
  • A custom UITableViewHeaderFooterView is the most reliable way to control size, weight, and spacing.
  • Register header views once and reuse them for smooth scrolling performance.
  • Use UIFontMetrics and Dynamic Type support to avoid accessibility regressions.
  • Remove conflicting header APIs and test with larger text settings before shipping.

Course illustration
Course illustration

All Rights Reserved.