UITableView
UITableViewStylePlain
floating headers
iOS development
Swift programming

Is it possible to disable floating headers in UITableView with UITableViewStylePlain?

Master System Design with Codemia

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

Introduction

In a plain-style UITableView, section headers are designed to stick to the top as you scroll. That behavior is built into UITableView.Style.plain, which is why developers looking for a simple disable floating headers flag usually come up empty.

So the honest answer is: not cleanly, not as a first-class option. If you want non-floating section headers, the best solution is usually to change the table style or use a different list implementation. If you must keep plain style, there are hacks that can reduce the sticky effect.

Why Plain Tables Float Headers

UITableView.Style.plain treats section headers as context markers for the rows currently visible. iOS keeps the current section header pinned until the next section pushes it away.

That means the floating behavior is not a side effect of your delegate methods. It is part of the style's default scrolling behavior.

By contrast, grouped and inset-grouped tables behave differently and often match the static header look people actually want.

The Best Option: Use a Different Style

If you are still early in implementation, switching styles is cleaner than fighting plain:

swift
let tableView = UITableView(frame: .zero, style: .grouped)

or, on newer iOS versions where it fits the design:

swift
let tableView = UITableView(frame: .zero, style: .insetGrouped)

This is the most maintainable answer because it uses platform behavior instead of working around it.

If You Must Keep Plain Style

If changing style is not acceptable, developers sometimes fake non-floating headers by adjusting content inset during scrolling:

swift
1import UIKit
2
3final class ViewController: UIViewController, UITableViewDelegate {
4    @IBOutlet weak var tableView: UITableView!
5
6    func scrollViewDidScroll(_ scrollView: UIScrollView) {
7        let headerHeight = tableView.sectionHeaderHeight
8
9        if scrollView.contentOffset.y >= 0 && scrollView.contentOffset.y <= headerHeight {
10            scrollView.contentInset.top = -scrollView.contentOffset.y
11        } else if scrollView.contentOffset.y > headerHeight {
12            scrollView.contentInset.top = -headerHeight
13        }
14    }
15}

This old workaround can reduce the pinning effect, but it is exactly that: a workaround. It depends on scroll behavior details and can be brittle with dynamic header heights, safe-area adjustments, or future UIKit changes.

Another Option: Avoid Section Headers Entirely

If the headers are only decorative, you can remove section headers and render your own non-sticky labels as ordinary cells:

swift
func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

Then create cells that visually separate groups. This is sometimes simpler than wrestling with native section-header behavior.

The tradeoff is that you lose some semantic structure and convenience from the section-based API.

Modern Alternative: Collection View Lists

For new work, a UICollectionView list layout often gives better control than a legacy table-view hack. You can model sections, headers, and appearance more flexibly while staying within Apple's modern list APIs.

A basic list layout:

swift
1import UIKit
2
3let config = UICollectionLayoutListConfiguration(appearance: .plain)
4let layout = UICollectionViewCompositionalLayout.list(using: config)
5let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)

Whether that is better depends on the rest of the screen, but it is often a cleaner long-term choice if the table is heavily customized already.

Choose Based on the Real Requirement

Ask what you actually need:

  • section organization with normal iOS behavior
  • non-sticky visual separators
  • full custom scrolling layout

If the answer is just I do not want sticky headers, then .grouped, .insetGrouped, or fake header cells may be enough. If the answer is I need completely custom behavior, UICollectionView is often the better foundation.

Common Pitfalls

The most common mistake is searching for a hidden UITableView property that disables floating headers in plain style. There is no clean built-in switch for that behavior.

Another common problem is applying the scrollViewDidScroll workaround without considering safe-area insets or automatic row and header sizing. It may look correct on one device and behave oddly on another.

People also stick with plain out of habit when grouped or insetGrouped would match the design better and require no hacks.

Finally, if the screen is already heavily customized, continuing to fight UITableView can be more expensive than moving to a collection-view list.

Summary

  • Plain-style UITableView headers are designed to float, and there is no clean built-in switch to disable that.
  • The best fix is usually to use .grouped or .insetGrouped if the design allows it.
  • If you must keep plain style, scrollViewDidScroll hacks can reduce the sticky effect but are fragile.
  • Fake header cells are a simple alternative when you do not need real section headers.
  • For heavily customized lists, UICollectionView is often the cleaner long-term solution.

Course illustration
Course illustration

All Rights Reserved.