Create a custom segmented control in SwiftUI
In this tutorial, we will see how to create a custom segment control in SwiftUI.
A segment control is like a row of buttons or tabs that lets us pick from different options. We have probably seen it before, it is a way to choose one thing from a bunch of choices. We can only pick one option at a time, and each button or segment stands for a different choice.
Create segment control
First of all, I will create a picker using the Picker view and apply the pickerStyle() modifier with the parameter .segmented on the Picker view to create a segment control.
You can use the SegmentedPickerStyle() instead of the .segmented style if you are using Xcode 12.
Example
import SwiftUI
struct ContentView: View {
// State variable to keep track of the selected color index
@State private var selectedColor = 0
// Array of colors
let colors = ["Red", "Blue", "Green"]
var body: some View {
VStack(spacing: 30) {
// Picker view allowing the user to select a color
Picker("Selected Color", selection: $selectedColor) {
// Creating segments for each color using a ForEach loop
ForEach(0..<colors.count, id: \.self) { index in
// Displaying the color names as segments
Text(colors[index]).tag(index)
}
}
// Setting the picker style to segmented
.pickerStyle(.segmented)
// Displaying the selected color based on the index
Text("Selected Color: \(colors[selectedColor])")
.font(.title3)
}
}
}In the above program, I have declared the colors array to store different color names like “Red,” “Blue,” and “Green.”
Inside the Picker, I used a ForEach loop to make Text labels for each color in the array. These labels become the clickable options in the segmented control.
Then, I have stored the selected segment index in the selectedColor variable.
I have also used a Text view to display the selected color below the segment control based on the selectedColor variable.
Output:

Create custom segment control
Creating a custom segmented control means designing a view that will display the segmented control’s user interface.
Now, follow the steps below to create a custom segment control in SwiftUI.
Define the Custom Segmented Control View
First of all, I will define a custom struct, this struct will represent our custom segmented control.
This struct will contain properties for the selected option index, an array of options, a base color, and a corner radius.
Example
import SwiftUI
struct MySegmentedControl: View {
@Binding var selectedOptionIndex: Int
var options: [String]
let baseColor = Color.mint
let cornerRadius: CGFloat = 20
var body: some View {
// Implementation of the segmented control UI
}
}
In the above code, I have created a custom struct called MySegmentedControl to represent a segmented control with customizable options and appearance.
Inside the MySegmentedControl struct, I have used a @Binding property to track the selected option index, an array options to store the available options, and styling parameters such as baseColor and cornerRadius.
Implementation of the segmented control UI
Now, within the body property of MySegmentedControl, I will build the UI for the segmented control.
This is where I will create the structure and appearance of the segmented control using SwiftUI components like HStack, Button, and others.
Example
HStack(spacing: 10) {
ForEach(options.indices, id: \.self) { index in
// Create a button for each option
Button(action: {
// Apply animation when tapped
withAnimation(.easeInOut(duration: 0.3)) {
// Update the selected index when the button is tapped
selectedOptionIndex = index
}
}) {
ZStack {
// Background
RoundedRectangle(cornerRadius: cornerRadius)
.fill(baseColor.opacity(0.2))
// Selected Highlight
RoundedRectangle(cornerRadius: cornerRadius)
.fill(baseColor) // Inner shape filled with base color
.padding(2) // Add padding to inner shape
.opacity(selectedOptionIndex == index ? 1 : 0.1) // Adjust opacity based on the selected index
}
.overlay(
Text(options[index]) // Display the text of the option
)
}
}
}
.frame(height: 40)
.cornerRadius(cornerRadius)
.padding()In the program above, I have used HStack to create a horizontal stack to align the segments horizontally.
I heve used a Button to represent each segment, so clicking the button would trigger the action.
Then, I have used the RoundedRectangle shape to visualize each segment, and the selected segment is highlighted separately.
I have also covered the Text on each side with an overlay to show the corresponding option label.
Implement the Main ContentView
Now, within the the ContentView I will define a @State property to track the index of the selected color and a array that will hold the available color option.
Then, within the body I will use the custom segmented control MySegmentedControl with binding to selectedIndex to select colors, and a Text view to display the currently selected color.
Example
struct ContentView: View {
// State variable to track the index of the selected color
@State var selectedIndex = 0
// Array holding available color options
let colorOptions = ["Red", "Blue", "Green"]
var body: some View {
VStack {
// Custom segmented control to select colors
MySegmentedControl(selectedOptionIndex: $selectedIndex, options: colorOptions)
// Text displaying the currently selected color
Text("Selected color: \(colorOptions[selectedIndex])")
.font(.title2)
}
}
}In the above code, I have defined a @State property called selectedIndex to track the index of the selected color.
I have also created an array called colorOptions to hold available color names such as “Red”, “Blue” and “Green”.
Here is the complete code below.
import SwiftUI
struct MySegmentedControl: View {
@Binding var selectedOptionIndex: Int
var options: [String]
let baseColor = Color.mint
let cornerRadius: CGFloat = 20
var body: some View {
HStack(spacing: 10) {
ForEach(options.indices, id: \.self) { index in
Button(action: {
withAnimation(.easeInOut(duration: 0.3)) {
selectedOptionIndex = index
}
}) {
ZStack {
RoundedRectangle(cornerRadius: cornerRadius)
.fill(baseColor.opacity(0.2))
RoundedRectangle(cornerRadius: cornerRadius)
.fill(baseColor)
.padding(2)
.opacity(selectedOptionIndex == index ? 1 : 0.1)
}
.overlay(
Text(options[index])
)
}
}
}
.frame(height: 40)
.cornerRadius(cornerRadius)
.padding()
}
}
struct ContentView: View {
@State var selectedIndex = 0
let colorOptions = ["Red", "Blue", "Green"]
var body: some View {
VStack {
MySegmentedControl(selectedOptionIndex: $selectedIndex, options: colorOptions)
Text("Selected color: \(colorOptions[selectedIndex])")
.font(.title2)
}
}
}Output:

Leave a Reply