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 segment control in SwiftUI

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:

Create custom segment control in SwiftUI

Leave a Reply

Your email address will not be published. Required fields are marked *