UIButton
iOS development
Swift programming
user interface
mobile app design

Keeping a UIButton selected after a touch

Master System Design with Codemia

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

Introduction

UIButton does not stay selected automatically after a tap just because it has a .selected visual state. You have to update isSelected yourself and provide different appearance values for the selected state so the user can actually see that the button stayed active.

Toggle the Selection State in the Action

The basic pattern is to flip isSelected when the button is tapped.

swift
1import UIKit
2
3final class ViewController: UIViewController {
4    @IBOutlet private weak var filterButton: UIButton!
5
6    override func viewDidLoad() {
7        super.viewDidLoad()
8
9        filterButton.setTitle("Filter", for: .normal)
10        filterButton.setTitle("Filter On", for: .selected)
11        filterButton.setTitleColor(.systemBlue, for: .normal)
12        filterButton.setTitleColor(.white, for: .selected)
13        filterButton.backgroundColor = .systemGray5
14    }
15
16    @IBAction private func didTapFilter(_ sender: UIButton) {
17        sender.isSelected.toggle()
18        sender.backgroundColor = sender.isSelected ? .systemBlue : .systemGray5
19    }
20}

This makes the button behave like a toggle control.

Configure State-Specific Appearance

Setting isSelected is only half the job. If the .selected appearance is not visibly different, the button technically changed state but the UI does not communicate it well.

Useful state-specific configuration includes:

  • title
  • title color
  • image
  • background image
  • custom configuration updates

For example:

swift
button.setImage(UIImage(systemName: "star"), for: .normal)
button.setImage(UIImage(systemName: "star.fill"), for: .selected)

That makes the selection state immediately obvious.

Keep Only One Button Selected

If the UI behaves more like tabs or radio buttons, only one button should remain selected at a time. In that case, do not just toggle the tapped button. Clear the others first.

swift
1@IBOutlet private weak var firstButton: UIButton!
2@IBOutlet private weak var secondButton: UIButton!
3@IBOutlet private weak var thirdButton: UIButton!
4
5@IBAction private func selectMode(_ sender: UIButton) {
6    let buttons = [firstButton, secondButton, thirdButton]
7
8    for button in buttons {
9        button?.isSelected = false
10        button?.backgroundColor = .systemGray5
11    }
12
13    sender.isSelected = true
14    sender.backgroundColor = .systemBlue
15}

This gives you a persistent selected button without leaving several buttons active at once.

Modern UIButton.Configuration

On newer iOS versions, buttons often use UIButton.Configuration. In that case, update the configuration based on state instead of manually setting every property after each tap.

swift
1button.configurationUpdateHandler = { button in
2    var config = button.configuration
3    config?.baseForegroundColor = button.isSelected ? .white : .systemBlue
4    config?.baseBackgroundColor = button.isSelected ? .systemBlue : .systemGray5
5    button.configuration = config
6}

Then toggle isSelected and call:

swift
button.setNeedsUpdateConfiguration()

This keeps state styling cleaner in modern UIKit code.

Programmatic Buttons Work the Same Way

If the button is created in code instead of Interface Builder, the state logic is unchanged. Add a target, toggle isSelected, and update the appearance in the same way. The persistence behavior comes from your state management, not from whether the button came from a storyboard or from code.

Common Pitfalls

The most common mistake is confusing .highlighted with .selected. Highlighted is a temporary pressed state during touch interaction. Selected is the persistent state you manage explicitly.

Another issue is setting isSelected = true without configuring any selected appearance. The button may stay selected internally, but the user has no visual cue.

A third pitfall is using toggle logic when the UI really needs mutually exclusive selection. In that case, clear sibling buttons and set only one active selection.

Finally, if the selected state should persist after leaving and returning to the screen, store that selection in your view model or controller state and reapply it during view setup.

Summary

  • A UIButton stays selected only if your code sets and keeps isSelected.
  • Configure the .selected appearance so the state is visible.
  • Use toggle behavior for on/off controls and exclusive-selection logic for tab-like groups.
  • Do not confuse the temporary .highlighted state with persistent selection.
  • Reapply selection from stored state if the UI needs to survive screen rebuilds.

Course illustration
Course illustration

All Rights Reserved.