Custom SwiftUI Toggle Styles
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 howdy@TrailingClosure.com.