Building a Customizable Segmented Control in SwiftUI

Peter Yaacoub •


Introduction

Segmented controls are a common UI element in iOS applications, allowing users to switch between different views or options. While SwiftUI provides a built-in Picker with a segmented style, creating a custom segmented control can offer more flexibility in design and functionality. In this article, we’ll explore how to build a custom segmented control in SwiftUI, drawing inspiration from my implementation in my app Catzumi.

Code Implementation

Let’s dive into the code for our custom SegmentedControl:

import SwiftUI

struct SegmentedControl: View {
    
    // MARK: - Properties
    
    @Binding var selection: Int
    var options: [String]
    
    // MARK: - Properties (Private)
    
    @StateObject private var themeManager = ThemeManager()
    
    // MARK: - Properties (Views)
    
    var body: some View {
        HStack(spacing: 0) {
            ForEach(options.indices, id: \.self) { index in
                Text(options[index])
                    .frame(maxWidth: .infinity)
                    .padding(8)
                    .apply { view in
                        if index == selection {
                            view
                                .foregroundStyle(themeManager.backgroundButton)
                                .background {
                                    Rectangle()
                                        .foregroundStyle(themeManager.foregroundButton)
                                }
                        } else {
                            view
                                .foregroundStyle(themeManager.foregroundButton)
                                .background {
                                    Rectangle()
                                        .foregroundStyle(themeManager.backgroundButton)
                                }
                        }
                    }
                    .onTapGesture {
                        UIImpactFeedbackGenerator(style: .light).impactOccurred()
                        withAnimation(.linear(duration: 0.1)) { selection = index }
                    }
            }
        }
        .padding(8)
        .border(themeManager.foregroundButton, width: 4)
    }
    
}

This implementation includes several key points:

  • Dynamic Options: The control accepts an array of strings, allowing for flexible segment options.
  • Theme Integration: It uses a ThemeManager to ensure consistent styling across the app.
  • Interactive Feedback: Haptic feedback is provided on selection changes.

Usage

Here’s how you can implement the custom SegmentedControl in your view:

import SwiftUI

struct ShopView: View {
    
    // MARK: - Properties (Private)
    
    @State private var section = 0
    
    // MARK: - Properties (View)
    
    var body: some View {
        ZStack {
            VStack(spacing: 0) {
                SegmentedControl(selection: $section, options: ["Cat", "Interface", "My Items"])
                    .padding()
                if section == 0 {
                    // ...
                } else if section == 1 {
                    // ...
                } else if section == 2 {
                    // ...
                }
            }
        }
    }

}

In this example from Catzumi, we’ve created a ShopView that uses our custom SegmentedControl to switch between different sections of the shop.

Enhancements

To further improve the segmented control, consider these additions:

  • Accessibility: Add voice-over labels and hints for each segment.
  • Animations: Implement smooth transitions between segments.
  • Responsiveness: Ensure the control adapts well to different screen sizes.

Conclusion

Building a custom segmented control in SwiftUI allows for greater flexibility and theming options in your app. By taking inspiration from my app Catzumi, we can create intuitive and visually appealing UI elements that enhance user engagement.

Remember, the key to a great custom control is balancing functionality with design. As you implement this in your own projects, don’t be afraid to experiment and adapt the control to fit your app’s unique style and needs.