UITableView
iOS Development
Swift
iOS UI Design
Mobile App Development

Grouped UITableview remove outer separator line

Master System Design with Codemia

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

Removing Outer Separator Lines from a Grouped UITableView

When using the grouped style of UITableView in iOS, the system draws separator lines around each section by default. These outer separators can clash with custom designs, especially when you want seamless section backgrounds or card-style layouts. This article covers multiple approaches to remove or hide these lines in Swift.

The grouped table view style was redesigned in iOS 13 to use an inset grouped appearance, but the separator behavior still causes visual noise in many custom layouts. Understanding how separators are rendered and which properties control them is essential for achieving a polished UI.

Understanding UITableView Separator Behavior

UITableView supports two primary styles:

  1. Plain: A flat scrolling list where separators appear between every row. Sections have sticky headers.
  2. Grouped: Rows are visually organized into sections with rounded corners (on iOS 13+) and spacing between groups.

In the grouped style, separators appear both between cells within a section and at the top and bottom edges of each section. The outer separators are the ones at the very top of the first cell and the bottom of the last cell in each section. These are the lines most developers want to remove.

Method 1: Set separatorStyle to .none

The simplest approach is to disable separators entirely on the table view:

swift
tableView.separatorStyle = .none

This removes all separator lines, including the ones between cells. If you still need separators between individual cells, you will need to add them manually using a custom view inside each cell.

swift
1class CustomCell: UITableViewCell {
2    private let separatorView = UIView()
3
4    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
5        super.init(style: style, reuseIdentifier: reuseIdentifier)
6
7        separatorView.backgroundColor = .separator
8        separatorView.translatesAutoresizingMaskIntoConstraints = false
9        contentView.addSubview(separatorView)
10
11        NSLayoutConstraint.activate([
12            separatorView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
13            separatorView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
14            separatorView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
15            separatorView.heightAnchor.constraint(equalToConstant: 1.0 / UIScreen.main.scale)
16        ])
17    }
18
19    required init?(coder: NSCoder) {
20        fatalError("init(coder:) has not been implemented")
21    }
22
23    func hideSeparator() {
24        separatorView.isHidden = true
25    }
26}

In your data source, hide the custom separator on the last cell of each section to avoid drawing the outer bottom line:

swift
1func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
2    let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
3    let isLastRow = indexPath.row == tableView.numberOfRows(inSection: indexPath.section) - 1
4    if isLastRow {
5        cell.hideSeparator()
6    }
7    return cell
8}

Method 2: Adjust separatorInset

You can push the separator off-screen by setting an extremely large left inset. This effectively hides the separator without disabling it:

swift
1func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
2    let isFirstRow = indexPath.row == 0
3    let isLastRow = indexPath.row == tableView.numberOfRows(inSection: indexPath.section) - 1
4
5    if isFirstRow || isLastRow {
6        // Push the separator off-screen
7        cell.separatorInset = UIEdgeInsets(top: 0, left: tableView.bounds.width, bottom: 0, right: 0)
8    } else {
9        // Normal separator inset
10        cell.separatorInset = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 0)
11    }
12}

This approach selectively hides separators only for the first and last rows of each section, preserving inter-cell separators.

Method 3: Use a Clear Separator Color

Another quick approach is to set the separator color to clear:

swift
tableView.separatorColor = .clear

This has the same visual effect as setting separatorStyle to .none, but the separator layout space is still reserved. This can lead to subtle spacing differences compared to fully removing separators.

Method 4: Custom Section Headers and Footers

If the outer separator lines are specifically at section boundaries, you can use zero-height section headers and footers to control the appearance:

swift
1func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
2    return UIView()
3}
4
5func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
6    return CGFloat.leastNormalMagnitude
7}
8
9func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
10    return UIView()
11}
12
13func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
14    return CGFloat.leastNormalMagnitude
15}

Returning CGFloat.leastNormalMagnitude instead of 0 avoids the system substituting a default height, which it does when it receives exactly zero.

Common Pitfalls

  • Cell reuse and separator state. Because cells are reused, you must reset the separator inset in willDisplay or cellForRowAt for every cell. Failing to do so can cause separators to appear or disappear unexpectedly as cells are recycled.
  • iOS version differences. The inset grouped style (.insetGrouped) introduced in iOS 13 has slightly different separator behavior than the classic .grouped style. Test on all supported versions.
  • Dark mode. Custom separator colors should adapt to light and dark mode. Use semantic colors like UIColor.separator instead of hardcoded values.
  • Accessibility. Separators serve as visual boundaries between content. When removing them, ensure your cells still have clear visual distinction through spacing, backgrounds, or shadows so that content boundaries remain obvious to all users.
  • Section index interference. If your table view includes a section index (the letter sidebar), large separator insets can interact poorly with the index column layout.

Summary

Removing outer separator lines from a grouped UITableView requires understanding how iOS draws separators at section boundaries. The most common approaches are disabling separators entirely with .none and adding custom dividers, selectively pushing separators off-screen using large inset values, or using clear separator colors. Each method has tradeoffs between simplicity and control. For the most polished result, combine separatorStyle = .none with custom separator views inside your cells so you have precise control over which dividers appear and how they are styled.


Course illustration
Course illustration

All Rights Reserved.