UITableViewCell
iOS Development
Swift
Customization
User Interface

Custom UITableViewCell selection style?

Master System Design with Codemia

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

Introduction

UITableViewCell already supports basic selection styling through selectionStyle, but real apps often need something more specific than the default gray highlight. The usual solution is to customize selectedBackgroundView, override selection state updates carefully, and make sure reuse does not leave cells in the wrong visual state.

The key point is that selection is a state problem, not just a color problem. If you style selection without respecting reuse and highlight transitions, the table view will eventually show inconsistent cells.

Start With the Built-In Styles

UIKit gives you a few built-in options:

swift
1cell.selectionStyle = .none
2cell.selectionStyle = .default
3cell.selectionStyle = .gray
4cell.selectionStyle = .blue

For simple apps, that may be enough. If you want a brand-specific color, rounded shape, or a custom selected layout, create your own selected background.

Use selectedBackgroundView

The cleanest way to customize selection appearance is usually selectedBackgroundView:

swift
1final class CustomCell: UITableViewCell {
2    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
3        super.init(style: style, reuseIdentifier: reuseIdentifier)
4
5        let selectedView = UIView()
6        selectedView.backgroundColor = UIColor.systemTeal.withAlphaComponent(0.18)
7        selectedBackgroundView = selectedView
8    }
9
10    required init?(coder: NSCoder) {
11        fatalError("init(coder:) has not been implemented")
12    }
13}

This tells UIKit what to show when the cell enters the selected state without forcing you to manually repaint everything in didSelectRowAt.

Custom Shape and Insets

If you want a rounded selection effect, build the background view explicitly:

swift
1final class RoundedSelectionCell: UITableViewCell {
2    private let roundedSelection = UIView()
3
4    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
5        super.init(style: style, reuseIdentifier: reuseIdentifier)
6
7        roundedSelection.backgroundColor = UIColor.systemBlue.withAlphaComponent(0.12)
8        roundedSelection.layer.cornerRadius = 12
9        roundedSelection.layer.masksToBounds = true
10        selectedBackgroundView = roundedSelection
11    }
12
13    override func layoutSubviews() {
14        super.layoutSubviews()
15        selectedBackgroundView?.frame = contentView.frame.insetBy(dx: 12, dy: 6)
16    }
17
18    required init?(coder: NSCoder) {
19        fatalError("init(coder:) has not been implemented")
20    }
21}

Using layoutSubviews() for sizing is important because cell bounds can change during layout, editing mode, or dynamic type updates.

Update Subviews When Selection Changes

If labels, icons, or other subviews should also react, override setSelected:

swift
1override func setSelected(_ selected: Bool, animated: Bool) {
2    super.setSelected(selected, animated: animated)
3
4    titleLabel.textColor = selected ? .systemBlue : .label
5    iconView.tintColor = selected ? .systemBlue : .secondaryLabel
6}

This is better than trying to set those properties only in didSelectRowAt because the cell itself remains responsible for rendering its own state.

You can also pair it with setHighlighted if the pressed state should differ from the selected state.

Reuse Matters

Cells are reused aggressively, so any custom selection styling must reset correctly. If the cell has custom colors, accessories, or hidden views, make sure they match the current state whenever the cell is configured.

swift
1override func prepareForReuse() {
2    super.prepareForReuse()
3    titleLabel.textColor = .label
4    iconView.tintColor = .secondaryLabel
5}

Then, in your cell configuration code, always set the full visible state instead of assuming reuse will preserve the correct one.

Common Pitfalls

Changing backgroundColor instead of selectedBackgroundView

If you directly mutate the cell background, the result often conflicts with highlight behavior and reuse. selectedBackgroundView is the more UIKit-friendly hook.

Forgetting to call super

When overriding setSelected or setHighlighted, call super first unless you have a specific reason not to. Otherwise the cell can drift away from UIKit's built-in state handling.

Ignoring accessibility contrast

A subtle highlight may look elegant and still be unreadable. Make sure selected text and background colors preserve enough contrast.

Styling once and forgetting reuse

If the cell changes subview colors on selection but never resets them, reused cells will eventually display stale state.

Summary

  • Built-in selectionStyle values are fine for simple tables.
  • For custom visuals, selectedBackgroundView is usually the cleanest entry point.
  • Override setSelected when labels or icons must react to selection state.
  • Respect cell reuse by resetting and reconfiguring the full visual state.
  • Selection styling is easiest to maintain when the cell owns its own appearance logic.

Course illustration
Course illustration

All Rights Reserved.