Creating a Segmented Control in SwiftUI

SegmentedControl.gif

How do you think you would create a segmented control in SwiftUI? Just use UISegmentedControl, right? Wrong! Similar to the change a while back with UIAlertView rolling into a style under UIAlertController, SwiftUI introduces a View called Picker that has different styles.

Setting Up

First let’s get set up with what data we’re going to display with our segmented control. It’s super nice outside and I only have a couple of weeks before I start training for triathlon season, so I’m going to pick triathlon sports and the distances for an Ironman triathlon race.

@State private var selectedSport = 0

private let triathlonSports = ["Swim", "Bike", "Run"]
private let ironmanDistances = ["2.4 miles", "112 miles", "26.2 miles"]

I also went ahead and added selectedSport as our state for keeping track of which segment is currently selected.

Creating a Segmented Control

Now that we have our data source set up we need to create our View. The goal for this example is to end up with a view like the gif above - Some text that shows the selected sport and how far you need to go, and the segmented control populated by the three sports.

VStack(spacing: 100) {
    Text("\(triathlonSports[selectedSport]) \(ironmanDistances[selectedSport])")
    Picker(selection: $selectedSport, label: Text("Select a Sport")) {
        ForEach(0 ..< triathlonSports.count) {
            Text(self.triathlonSports[$0])
        }
    }
}
.padding()

The first two lines create a VStack to hold the text and segmented control and add the text, then the rest sets up the segmented control itself. Let’s look a little closer at how the segmented control is created.

Picker(selection: $selectedSport, label: Text("Select a Sport")) { ... }

In order to create a segmented control you need to instantiate a Picker. The Picker’s first argument is a binding to a state property that keeps track of the selected segment, which in our case is the $selectedSport property. Next is a label to give the user a little extra information.

ForEach(0 ..< triathlonSports.count) {
    Text(self.triathlonSports[$0])
}

The rest of the creation of the Picker tells it what to display on each of the segments. ForEach is a way to iteratively create views in SwiftUI. What is happening here is basically the same as a for loop that goes from 0 to the number of items in the triathlonSports array, creating a new Text View for each index and setting the text to the matching sport.

Wheel.png

Oh but that doesn’t look right. That just looks like a regular old picker with an ugly label on the left. That’s because we haven’t changed the style to a segmented control! Update your Picker code to include the .pickerStyle modifier:

Picker(selection: $selectedSport, label: Text("Select a Sport")) {
    ForEach(0 ..< triathlonSports.count) {
        Text(self.triathlonSports[$0])
    }
}
.pickerStyle(SegmentedPickerStyle())

And voilà! The picker updates to a segmented control!

Segmented.png

Bonus - How to style a segmented control

Gray is too boring, so let’s spice things up a bit with some color! Because the segmented control we created is actually backed by UISegmentedControl from UIKit we can take advantage of appearance to update the look. So if, for example, you wanted a segmented control that has a blue selected color, white text for the selected segment, and blue text for all the other segments you could do so by adding this to your SwiftUI file:

init() {
    UISegmentedControl.appearance().selectedSegmentTintColor = .blue
    UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.white], for: .selected)
    UISegmentedControl.appearance().setTitleTextAttributes([.foregroundColor: UIColor.blue], for: .normal)
}
Previous
Previous

How to Use a Toggle in SwiftUI

Next
Next

Updating Your View for Dark Mode in SwiftUI