Custom SwiftUI Toggle Styles
Quick Tip SwiftUI

Custom SwiftUI Toggle Styles

Jean-Marc Boullianne
Jean-Marc Boullianne

My SwiftUI quick tip for this week covers custom Toggle Views! You can easily apply your own style to SwiftUI Toggles by using the ToggleStyle protocol. The best part is you don't need to worry about implementing any of the backing properties of the Toggle. Simply toggle the isOn property inside the Configuration instance that's passed from the makeBody(configuration:) function.

Create a Custom ToggleStyle

Start off by creating a new struct and make sure to inherit from the ToggleStyle Protocol. Then implement the makeBody(configuration:) function. This is where you'll construct your custom View to be shown in place of the default switch.

import SwiftUI 

struct MyToggleStyle: ToggleStyle {
    
    func makeBody(configuration: Configuration) -> some View {
    	// Insert custom View code here.
    }
    
}

Then apply your custom ToggleStyle in code to your Toggle like this. Simple as that.

Toggle(isOn: $active, label: {
	Text("Active")
})
.toggleStyle(MyToggleStyle())

If you found this tip helpful, please consider subscribing using this link, and if you aren't reading this on TrailingClosure.com, please come check us out sometime!

Examples

Here's a few examples I cooked up while writing this tutorial. Your styles can be subtle changes or redesign the entire toggle itself. Play around with it and have fun. See what works best for the style of your app.

CheckmarkToggleStyle


struct CheckmarkToggleStyle: ToggleStyle {
    
    func makeBody(configuration: Configuration) -> some View {
        HStack {
            configuration.label
            Spacer()
            Rectangle()
                .foregroundColor(configuration.isOn ? .green : .gray)
                .frame(width: 51, height: 31, alignment: .center)
                .overlay(
                    Circle()
                        .foregroundColor(.white)
                        .padding(.all, 3)
                        .overlay(
                            Image(systemName: configuration.isOn ? "checkmark" : "xmark")
                                .resizable()
                                .aspectRatio(contentMode: .fit)
                                .font(Font.title.weight(.black))
                                .frame(width: 8, height: 8, alignment: .center)
                                .foregroundColor(configuration.isOn ? .green : .gray)
                        )
                        .offset(x: configuration.isOn ? 11 : -11, y: 0)
                        .animation(Animation.linear(duration: 0.1))
                        
                ).cornerRadius(20)
                .onTapGesture { configuration.isOn.toggle() }
        }
    }
    
}

PowerToggleStyle

struct PowerToggleStyle: ToggleStyle {
    
    func makeBody(configuration: Configuration) -> some View {
        HStack {
            configuration.label
            Spacer()
            Rectangle()
                .foregroundColor(configuration.isOn ? .green : .gray)
                .frame(width: 51, height: 31, alignment: .center)
                .overlay(
                    Circle()
                        .foregroundColor(.white)
                        .padding(.all, 3)
                        .overlay(
                            GeometryReader { geo in
                                Path { p in
                                    if !configuration.isOn {
                                        p.addRoundedRect(in: CGRect(x: 20, y: 10, width: 10.5, height: 10.5), cornerSize: CGSize(width: 7.5, height: 7.5), style: .circular, transform: .identity)
                                    } else {
                                        p.move(to: CGPoint(x: 51/2, y: 10))
                                        p.addLine(to: CGPoint(x: 51/2, y: 31-10))
                                    }
                                }.stroke(configuration.isOn ? Color.green : Color.gray, lineWidth: 2)
                            }
                        )
                        .offset(x: configuration.isOn ? 11 : -11, y: 0)
                        .animation(Animation.linear(duration: 0.1))
                        
                ).cornerRadius(20)
                .onTapGesture { configuration.isOn.toggle() }
        }
    }
}

ImageToggleStyle

struct ImageToggleStyle: ToggleStyle {
    
    var onImageName: String
    var offImageName: String
    
    func makeBody(configuration: Configuration) -> some View {
        HStack {
            configuration.label
            Spacer()
            Image(configuration.isOn ? onImageName : offImageName)
                .resizable()
                .aspectRatio(contentMode: .fill)
                .frame(width: 51, height: 31, alignment: .center)
                .overlay(
                    Circle()
                        .foregroundColor(.white)
                        .padding(.all, 3)
                        .offset(x: configuration.isOn ? 11 : -11, y: 0)
                        .animation(Animation.linear(duration: 0.1))
                ).cornerRadius(20)
                .onTapGesture { configuration.isOn.toggle() }
        }
        
            
    }
}

Show us what you've made!

We want to see what you've made using this tutorial! Send us pics! Find us on Twitter @TrailingClosure, on Instagram, or email us at [email protected].