SwiftUI · · 5 min read

Using ToggleStyle to Build Custom Toggles in SwiftUI

Using ToggleStyle to Build Custom Toggles in SwiftUI

Toggle in SwiftUI is one of the most used views that it allows users to switch between two states, either on or off. This control is a great way to provide a simple and intuitive interface for your users, making it a popular choice for many developers.For example, you might use a toggle in your app to turn on and off certain features, or to switch between different modes or options. Toggle is very easy to implement in your SwiftUI applications. With the ToggleStyle protocol, you can create toggles that match your app’s design and branding.

In this tutorial, I will walk you through this Toggle view and show you how to build several custom toggles using ToggleStyle.

The Basics of Toggle

SwiftUI Sample Toggles

There are multiple ways to use the Toggle view in SwiftUI. The basic usage is like below:

struct ContentView: View {
    @State private var isEnabled = false

    var body: some View {
        Toggle("Airplane Mode", isOn: $isEnabled)
    }
}

Here, we initialize a Toggle view with a text description and a binding. The state of the toggle is controlled by the state variable. This renders a basic toggle with a text label as shown above.

If you want to customize the text style, Toggle also provides an another initializer. You can set the visual appearance of the Text view in the closure like this:

Toggle(isOn: $isEnabled) {
    Text("Airplane mode")
        .font(.system(.title, design: .rounded))
        .bold()
}

You can further customize the description to include an image like this:

Toggle(isOn: $isEnabled) {
    HStack {
        Text("Airplane mode")
        Image(systemName: "airplane")

    }
    .font(.system(size: 20))
}

This results in a switch with a more visually appealing design.

swiftui-switch-with-image

Changing the Toggle Style

By default, the Toggle view uses the switch style. You can use the .toggleStyle modifier to change the style from switch to button:

.toggleStyle(.button)

Say, if you want to create a bookmark button, you can create it using the code below:

struct ContentView: View {
    @State private var isBookmarked = false

    var body: some View {
        Toggle(isOn: $isBookmarked) {
            Image(systemName: isBookmarked ? "bookmark.fill" : "bookmark")
                .font(.system(size: 50))
        }
        .tint(.green)
        .toggleStyle(.button)
        .clipShape(Circle())
    }
}

The usage is very similar except that we tell Toggle to use the .button toggle style using the .toggleStyle modifier. In this case, instead of displaying the toggle as a switch, SwiftUI shows a button that alters its appearance depending on the state.

swiftui-toggle-button-style

Using ToggleStyle to Create a Custom Toggle

In most cases, the default toggle style is sufficient, but there may be instances where you want to create a custom style to match the aesthetics of your app better. Now let’s see how to create our own custom toggle. In brief, you adopt the ToggleStyle protocol and implement the required makeBody(configuration:) function to create your own style:

struct CustomToggleStyle: ToggleStyle {
    func makeBody(configuration: Configuration) -> some View {
        // Your implementation
    }
}

Let’s start with a simple task. We’re going to construct a toggle switch with a customizable background color and symbol.

swiftui-togglestyle-example

To do that, we create a new struct named SymbolToggleStyle that conforms to the ToggleStyle protocol:

struct SymbolToggleStyle: ToggleStyle {

    var systemImage: String = "checkmark"
    var activeColor: Color = .green

    func makeBody(configuration: Configuration) -> some View {
        HStack {
            configuration.label

            Spacer()

            RoundedRectangle(cornerRadius: 30)
                .fill(configuration.isOn ? activeColor : Color(.systemGray5))
                .overlay {
                    Circle()
                        .fill(.white)
                        .padding(3)
                        .overlay {
                            Image(systemName: systemImage)
                                .foregroundColor(configuration.isOn ? activeColor : Color(.systemGray5))
                        }
                        .offset(x: configuration.isOn ? 10 : -10)

                }
                .frame(width: 50, height: 32)
                .onTapGesture {
                    withAnimation(.spring()) {
                        configuration.isOn.toggle()
                    }
                }
        }
    }
}

The struct takes in two parameters: one is the image name of the symbol and the other is the color of the switch. In the makeBody function, we build the toggle from the ground up. This includes placing the text label on one side and the switch on the other.

The configuration parameter gives us two pieces of information: the text label (i.e. configuration.label) and the state (i.e. configuration.isOn) of the toggle. In the code above, we arrange the text label and the rounded rectangle using HStack. We built the switch by overlaying a circle on top of it. To display a symbol, we further overlay an image view on top of the circle. By default, the image is set to display a checkmark.

As mentioned, we actually create the switch from scratch. This is why we need to handle the tap gesture and toggle the state of the switch. And, when the state changes, we move the circle by changing the offset value.

To use this custom toggle style, we can apply the toggleStyle modifier like this:

Toggle(isOn: $isEnabled) {
    Text("Airplane mode")
}
.toggleStyle(SymbolToggleStyle(systemImage: "airplane", activeColor: .purple))

This creates a toggle using the SymbolToggleStyle.

Creating an Animated Image Toggle

swiftui-toggle-light-dark

Now let us construct another toggle style that shows different images for its enabled and disabled states. We’ll use these two images for the project:

Once you downloaded the images, import them to the asset catalog of your SwiftUI project. We then create a new toggle style like this:

struct ImageToggleStyle: ToggleStyle {

    var onImage = "dark"
    var offImage = "light"

    func makeBody(configuration: Configuration) -> some View {

        HStack {
            configuration.label

            Spacer()

            RoundedRectangle(cornerRadius: 30)
                .fill(configuration.isOn ? .black : Color(.systemGray5))
                .overlay {
                    Image(configuration.isOn ? onImage : offImage)
                        .resizable()
                        .scaledToFill()
                        .clipShape(Circle())
                        .padding(5)
                        .rotationEffect(.degrees(configuration.isOn ? 0 : -360))
                        .offset(x: configuration.isOn ? 10 : -10)
                }
                .frame(width: 50, height: 32)
                .onTapGesture {
                    withAnimation(.spring()) {
                        configuration.isOn.toggle()
                    }
                }
        }
    }
}

This ImageToggleStyle struct accepts two parameters for the ON & OFF images. The implementation of the switch is very similar to that we discussed before. Instead of using a Circle view to create the switch, we use an image view to display the ON & OFF images.

We also added the rotationEffect modifier to animate the transition of the switch between the states.

Again, to use this toggle style, you can simply attach the .toggleStyle modifier to the Toggle view. Here is an example:

VStack {
    Toggle(isOn: $isEnabled) {
        Text("Light/Dark mode")
            .foregroundColor(isEnabled ? .white : .black)
    }
    .toggleStyle(ImageToggleStyle())
    .padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(isEnabled ? .black.opacity(0.6) : .white)

Accessing the Toggle Style with Ease

To simplify the access of the custom toggle style, you can add a static variable in an extension to ToggleStyle as follows:

extension ToggleStyle where Self == ImageToggleStyle {

    static var image: ImageToggleStyle { .init() }
}

In this case, you can apply the toggle style using the dot syntax:

Toggle(isOn: $isEnabled) {
    Text("Light/Dark mode")
        .foregroundColor(isEnabled ? .white : .black)
}
.toggleStyle(.image)

What’s Next

In this tutorial, we’ve showed you the techniques of creating custom toggle styles in SwiftUI. With the help of the ToggleStyle protocol, you can craft visually appealing toggles that stand out from the crowd. If you’re eager to learn more about SwiftUI and the ToggleStyle protocol, be sure to check out our “Mastering SwiftUI” book for even more tips and tricks, plus complete source code.

Read next