iOS development
UISegmentedControl
view switching
Swift programming
mobile app interface

How do I use a UISegmentedControl to switch views?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

To switch views with a UISegmentedControl in iOS, add a target action that responds to the .valueChanged event, then show/hide child views (or swap child view controllers) based on the selectedSegmentIndex. The segmented control acts as a tab-like selector — each segment maps to a different content view. The two main approaches are toggling view visibility (simple) and using container view controllers (scalable for complex content).

Basic Setup with View Toggling

swift
1import UIKit
2
3class ViewController: UIViewController {
4    let segmentedControl = UISegmentedControl(items: ["First", "Second", "Third"])
5    let firstView = UIView()
6    let secondView = UIView()
7    let thirdView = UIView()
8
9    override func viewDidLoad() {
10        super.viewDidLoad()
11        setupSegmentedControl()
12        setupContentViews()
13    }
14
15    func setupSegmentedControl() {
16        segmentedControl.selectedSegmentIndex = 0
17        segmentedControl.addTarget(self, action: #selector(segmentChanged), for: .valueChanged)
18
19        segmentedControl.translatesAutoresizingMaskIntoConstraints = false
20        view.addSubview(segmentedControl)
21
22        NSLayoutConstraint.activate([
23            segmentedControl.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16),
24            segmentedControl.centerXAnchor.constraint(equalTo: view.centerXAnchor)
25        ])
26    }
27
28    func setupContentViews() {
29        firstView.backgroundColor = .systemRed
30        secondView.backgroundColor = .systemBlue
31        thirdView.backgroundColor = .systemGreen
32
33        for contentView in [firstView, secondView, thirdView] {
34            contentView.translatesAutoresizingMaskIntoConstraints = false
35            view.addSubview(contentView)
36            NSLayoutConstraint.activate([
37                contentView.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 16),
38                contentView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
39                contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
40                contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
41            ])
42        }
43
44        // Show only the first view initially
45        segmentChanged()
46    }
47
48    @objc func segmentChanged() {
49        firstView.isHidden = segmentedControl.selectedSegmentIndex != 0
50        secondView.isHidden = segmentedControl.selectedSegmentIndex != 1
51        thirdView.isHidden = segmentedControl.selectedSegmentIndex != 2
52    }
53}

When the user taps a segment, segmentChanged hides all views except the one matching the selected index.

Container View Controller Approach

For complex content, swap child view controllers instead of simple views:

swift
1class TabContainerViewController: UIViewController {
2    let segmentedControl = UISegmentedControl(items: ["Profile", "Settings", "Activity"])
3    let containerView = UIView()
4
5    private var childVCs: [UIViewController] = []
6    private var currentVC: UIViewController?
7
8    override func viewDidLoad() {
9        super.viewDidLoad()
10
11        childVCs = [ProfileViewController(), SettingsViewController(), ActivityViewController()]
12
13        setupUI()
14        segmentedControl.selectedSegmentIndex = 0
15        segmentedControl.addTarget(self, action: #selector(segmentChanged), for: .valueChanged)
16        switchToVC(at: 0)
17    }
18
19    func setupUI() {
20        segmentedControl.translatesAutoresizingMaskIntoConstraints = false
21        containerView.translatesAutoresizingMaskIntoConstraints = false
22        view.addSubview(segmentedControl)
23        view.addSubview(containerView)
24
25        NSLayoutConstraint.activate([
26            segmentedControl.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8),
27            segmentedControl.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
28            segmentedControl.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
29            containerView.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 8),
30            containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
31            containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
32            containerView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
33        ])
34    }
35
36    @objc func segmentChanged() {
37        switchToVC(at: segmentedControl.selectedSegmentIndex)
38    }
39
40    func switchToVC(at index: Int) {
41        let newVC = childVCs[index]
42
43        // Remove current child VC
44        currentVC?.willMove(toParent: nil)
45        currentVC?.view.removeFromSuperview()
46        currentVC?.removeFromParent()
47
48        // Add new child VC
49        addChild(newVC)
50        containerView.addSubview(newVC.view)
51        newVC.view.frame = containerView.bounds
52        newVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
53        newVC.didMove(toParent: self)
54
55        currentVC = newVC
56    }
57}

This properly manages the view controller lifecycle — each child VC receives viewWillAppear/viewDidDisappear events.

SwiftUI Implementation

swift
1import SwiftUI
2
3struct ContentView: View {
4    @State private var selectedTab = 0
5
6    var body: some View {
7        VStack {
8            Picker("View", selection: $selectedTab) {
9                Text("First").tag(0)
10                Text("Second").tag(1)
11                Text("Third").tag(2)
12            }
13            .pickerStyle(.segmented)
14            .padding()
15
16            // Switch content based on selection
17            switch selectedTab {
18            case 0:
19                FirstView()
20            case 1:
21                SecondView()
22            default:
23                ThirdView()
24            }
25
26            Spacer()
27        }
28    }
29}
30
31struct FirstView: View {
32    var body: some View {
33        Text("First View Content")
34            .font(.title)
35            .foregroundColor(.red)
36    }
37}

In SwiftUI, Picker with .segmented style replaces UISegmentedControl. The @State variable triggers a view update when the selection changes.

Customizing Appearance

swift
1// UIKit customization
2let control = UISegmentedControl(items: ["One", "Two", "Three"])
3
4// Tint color (entire control)
5control.selectedSegmentTintColor = .systemBlue
6
7// Text attributes for normal state
8control.setTitleTextAttributes([
9    .foregroundColor: UIColor.gray,
10    .font: UIFont.systemFont(ofSize: 14)
11], for: .normal)
12
13// Text attributes for selected state
14control.setTitleTextAttributes([
15    .foregroundColor: UIColor.white,
16    .font: UIFont.boldSystemFont(ofSize: 14)
17], for: .selected)
18
19// Background color
20control.backgroundColor = UIColor.systemGray6

With Navigation Bar

swift
1class NavBarSegmentedVC: UIViewController {
2    override func viewDidLoad() {
3        super.viewDidLoad()
4
5        let segmented = UISegmentedControl(items: ["All", "Favorites", "Recent"])
6        segmented.selectedSegmentIndex = 0
7        segmented.addTarget(self, action: #selector(segmentChanged), for: .valueChanged)
8
9        // Place in navigation bar's title view
10        navigationItem.titleView = segmented
11    }
12
13    @objc func segmentChanged(_ sender: UISegmentedControl) {
14        switch sender.selectedSegmentIndex {
15        case 0: showAllItems()
16        case 1: showFavorites()
17        case 2: showRecent()
18        default: break
19        }
20    }
21}

Placing the segmented control in navigationItem.titleView is a common iOS pattern (used in Apple's own apps like Mail and Files).

Common Pitfalls

  • Not calling didMove(toParent:): When using child view controllers, you must call addChild() before adding the view and didMove(toParent: self) after. Skipping this breaks the view controller lifecycle — viewWillAppear and viewDidAppear will not be called.
  • Memory from keeping all views loaded: The simple toggle approach loads all views at once. For heavy content (images, web views, maps), this wastes memory. Use the container VC approach and optionally release off-screen VCs.
  • Missing .valueChanged event: Adding the target with the wrong event (e.g., .touchUpInside) means the action never fires. UISegmentedControl fires .valueChanged, not touch events.
  • Forgetting selectedSegmentIndex = 0: The default selected index is -1 (none selected). If you do not set an initial selection, no segment appears selected and no content is shown until the user taps.
  • Animation when switching: Abruptly showing/hiding views feels jarring. Add a UIView.transition or UIView.animate for smooth crossfade: UIView.transition(with: containerView, duration: 0.2, options: .transitionCrossDissolve, animations: { ... }).

Summary

  • Handle the .valueChanged event on UISegmentedControl to detect selection changes
  • For simple content, toggle isHidden on child views based on selectedSegmentIndex
  • For complex content, use container view controllers with proper child VC lifecycle management
  • In SwiftUI, use Picker with .segmented style bound to a @State variable
  • Place the segmented control in navigationItem.titleView for navigation-bar integration
  • Always set selectedSegmentIndex to a valid value in viewDidLoad

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.