iOS
UISegmentedControl
Swift
programming
app development

How do I switch UISegmentedControl programmatically?

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 a UISegmentedControl segment programmatically in Swift, set its selectedSegmentIndex property. Setting this property changes the visual selection but does not fire the .valueChanged action by default. If you need the value-changed handler to execute, call sendActions(for: .valueChanged) after setting the index. In SwiftUI, bind a @State variable to a Picker with .segmented style and update the variable to switch segments.

UIKit: Setting selectedSegmentIndex

swift
1class ViewController: UIViewController {
2
3    let segmentedControl = UISegmentedControl(items: ["Daily", "Weekly", "Monthly"])
4
5    override func viewDidLoad() {
6        super.viewDidLoad()
7
8        segmentedControl.selectedSegmentIndex = 0 // Default to "Daily"
9        segmentedControl.addTarget(self, action: #selector(segmentChanged(_:)),
10                                   for: .valueChanged)
11        view.addSubview(segmentedControl)
12    }
13
14    @objc func segmentChanged(_ sender: UISegmentedControl) {
15        switch sender.selectedSegmentIndex {
16        case 0: showDailyData()
17        case 1: showWeeklyData()
18        case 2: showMonthlyData()
19        default: break
20        }
21    }
22
23    // Switch programmatically
24    func selectWeekly() {
25        segmentedControl.selectedSegmentIndex = 1
26        // Note: this does NOT trigger segmentChanged automatically
27    }
28}

Triggering the Value Changed Action

Setting selectedSegmentIndex does not fire .valueChanged. To trigger the handler:

swift
1func selectWeeklyAndNotify() {
2    segmentedControl.selectedSegmentIndex = 1
3    segmentedControl.sendActions(for: .valueChanged) // Fires segmentChanged
4}

Or call your handler directly:

swift
1func selectWeeklyAndUpdate() {
2    segmentedControl.selectedSegmentIndex = 1
3    segmentChanged(segmentedControl) // Call handler directly
4}

Deselecting All Segments

Set selectedSegmentIndex to -1 to deselect all segments:

swift
segmentedControl.selectedSegmentIndex = UISegmentedControl.noSegment // -1

This is useful when the segmented control should start with no selection, requiring the user to make an explicit choice.

Setting Up from Storyboard

swift
1class ViewController: UIViewController {
2
3    @IBOutlet weak var filterControl: UISegmentedControl!
4
5    override func viewDidLoad() {
6        super.viewDidLoad()
7        filterControl.selectedSegmentIndex = 0
8    }
9
10    @IBAction func filterChanged(_ sender: UISegmentedControl) {
11        let selectedTitle = sender.titleForSegment(at: sender.selectedSegmentIndex)
12        print("Selected: \(selectedTitle ?? "")")
13
14        // React to selection
15        updateContent(for: sender.selectedSegmentIndex)
16    }
17
18    // Switch programmatically from another action
19    @IBAction func resetTapped(_ sender: UIButton) {
20        filterControl.selectedSegmentIndex = 0
21        filterChanged(filterControl) // Manually trigger update
22    }
23}

Dynamic Segments

Add, remove, and modify segments at runtime:

swift
1let control = UISegmentedControl(items: ["One", "Two"])
2
3// Insert a segment
4control.insertSegment(withTitle: "Three", at: 2, animated: true)
5
6// Insert with an image
7control.insertSegment(with: UIImage(systemName: "star"), at: 0, animated: true)
8
9// Remove a segment
10control.removeSegment(at: 1, animated: true)
11
12// Remove all segments
13control.removeAllSegments()
14
15// Change a segment title
16control.setTitle("Updated", forSegmentAt: 0)
17
18// Enable/disable a specific segment
19control.setEnabled(false, forSegmentAt: 2)

Customizing Appearance

swift
1let control = UISegmentedControl(items: ["Light", "Dark", "Auto"])
2
3// Tint color (iOS 12 and earlier)
4control.tintColor = .systemBlue
5
6// Background and selected colors (iOS 13+)
7control.selectedSegmentTintColor = .systemBlue
8control.backgroundColor = .systemGray6
9
10// Text attributes for normal state
11control.setTitleTextAttributes([
12    .foregroundColor: UIColor.gray,
13    .font: UIFont.systemFont(ofSize: 14)
14], for: .normal)
15
16// Text attributes for selected state
17control.setTitleTextAttributes([
18    .foregroundColor: UIColor.white,
19    .font: UIFont.boldSystemFont(ofSize: 14)
20], for: .selected)

SwiftUI: Picker with Segmented Style

swift
1struct ContentView: View {
2    @State private var selectedFilter = 0
3
4    var body: some View {
5        VStack {
6            Picker("Filter", selection: $selectedFilter) {
7                Text("Daily").tag(0)
8                Text("Weekly").tag(1)
9                Text("Monthly").tag(2)
10            }
11            .pickerStyle(.segmented)
12
13            // Content changes based on selection
14            switch selectedFilter {
15            case 0: DailyView()
16            case 1: WeeklyView()
17            case 2: MonthlyView()
18            default: EmptyView()
19            }
20        }
21    }
22
23    // Switch programmatically
24    func selectMonthly() {
25        selectedFilter = 2 // Automatically updates the Picker
26    }
27}

Using an Enum for Type Safety

swift
1enum TimeFilter: String, CaseIterable {
2    case daily = "Daily"
3    case weekly = "Weekly"
4    case monthly = "Monthly"
5}
6
7struct ContentView: View {
8    @State private var selectedFilter: TimeFilter = .daily
9
10    var body: some View {
11        Picker("Filter", selection: $selectedFilter) {
12            ForEach(TimeFilter.allCases, id: \.self) { filter in
13                Text(filter.rawValue).tag(filter)
14            }
15        }
16        .pickerStyle(.segmented)
17
18        Button("Show Monthly") {
19            selectedFilter = .monthly
20        }
21    }
22}

Common Pitfalls

  • Assuming selectedSegmentIndex triggers .valueChanged: Setting the index programmatically does not fire the action. Call sendActions(for: .valueChanged) or invoke your handler directly if you need the associated logic to run.
  • Using an out-of-range index: Setting selectedSegmentIndex to an index beyond the number of segments crashes with an out-of-bounds exception. Always validate the index: if index < segmentedControl.numberOfSegments.
  • Forgetting UISegmentedControl.noSegment for deselection: Setting the index to an arbitrary negative number may not work on all iOS versions. Use UISegmentedControl.noSegment (which equals -1) for cross-version safety.
  • Not updating content when switching programmatically: When a user taps a segment, .valueChanged fires and your handler runs. When you switch programmatically, you must manually trigger the content update since the handler does not fire automatically.
  • Using wrong Picker style in SwiftUI: Picker defaults to a wheel or menu style. You must explicitly add .pickerStyle(.segmented) to get the segmented control appearance.

Summary

  • Set segmentedControl.selectedSegmentIndex = n to switch segments programmatically in UIKit
  • Call sendActions(for: .valueChanged) after setting the index if you need the action handler to fire
  • Use UISegmentedControl.noSegment (-1) to deselect all segments
  • In SwiftUI, use Picker with .pickerStyle(.segmented) and bind to a @State variable
  • Use an enum with CaseIterable in SwiftUI for type-safe segment values

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.