ToggleStyle
allows us to customize the Toggle
view. In order to build custom Toggle
, we simply need to create a new type conforming to the ToggleStyle
modifier which returns the custom view for toggle in makeBody(configuration:)
function.
In this article, we will build a few custom toggles so let’s start with the most basic one to understand the protocol better.
We will start with the default Toggle in the view.
struct DevTechieToggleStyleExample: View {
@State private var agree = false
var body: some View {
VStack {
Text("DevTechie")
.font(.largeTitle)
Text("DevTechie.com helps me learn iOS Development by building real examples.")
.padding()
.background(agree ? Color.orange : Color.gray.opacity(0.5), in: RoundedRectangle(cornerRadius: 20))
.animation(.easeInOut, value: agree)
Toggle("Do you agree?", isOn: $agree)
}
.padding()
}
}
We will create a new struct conforming ToggleStyle protocol. As mentioned earlier, this protocol only requires one function called makeBody(configuration:)
to be implemented.
Configuration
passed inside the makeBody(configuration:)
is ToggleStyleConfiguration
type and has
Label
: this is a view that describes the effect of switching the toggle between states.
$isOn
: is a binding to let us listen to the changes in the toggle’s isOn property.
isOn
: is the property which indicates whether the toggle is on or off.
With all this information, we will put together our custom toggleStyle.
struct DevTechieCustomToggleStyle: ToggleStyle {
func makeBody(configuration: Configuration) -> some View {
HStack {
configuration.label
Spacer()
Capsule()
.fill(Color.gray.opacity(0.4))
.frame(width: 50, height: 30)
.overlay(
Circle()
.foregroundColor(configuration.isOn ? Color.green : Color.red)
.padding(.all, 3)
.offset(x: configuration.isOn ? 10 : -10, y: 0)
)
.onTapGesture {
withAnimation(Animation.linear(duration: 0.1)) {
configuration.isOn.toggle()
}
}
}
}
}
Using this style is easy, just call it inside the toggleStyle modifier.
struct DevTechieToggleStyleExample: View {
@State private var agree = false
var body: some View {
VStack {
Text("DevTechie")
.font(.largeTitle)
Text("DevTechie.com helps me learn iOS Development by building real examples.")
.padding()
.background(agree ? Color.orange : Color.gray.opacity(0.5), in: RoundedRectangle(cornerRadius: 20))
.animation(.easeInOut, value: agree)
Toggle("Do you agree?", isOn: $agree)
.toggleStyle(DevTechieCustomToggleStyle())
}
.padding()
}
}
Let’s create another example. This time, we will use SF Symbol images and bit of SwiftUI view alignment technique to draw user’s attention onto our message.
struct DevTechieFlashLightStyle: ToggleStyle {
static let backgroundColor = Color(.label)
static let switchColor = Color(.systemBackground)
func makeBody(configuration: Configuration) -> some View {
VStack {
ZStack {
Image(systemName: "arrowtriangle.down.fill")
.frame(width: 30, height: 30, alignment: .top)
.font(.system(size: 50))
.offset(y: -30)
.foregroundColor(.yellow.opacity(0.5))
.opacity(configuration.isOn ? 1 : 0)
Image(systemName: configuration.isOn ? "flashlight.on.fill": "flashlight.off.fill")
.font(.system(size: 50))
.opacity(configuration.isOn ? 1 : 0.7)
}
.onTapGesture(perform: {
withAnimation(Animation.easeInOut) {
configuration.isOn.toggle()
}
})
}
}
}
Replace old style with new one.
Toggle("Do you agree?", isOn: $agree).toggleStyle(DevTechieFlashLightStyle())
Build and run
Let’s build one more toggle style with SF Symbols.
public struct DayNightToggleStyle: ToggleStyle {
public func makeBody(configuration: Configuration) -> some View {
HStack {
configuration.label
Spacer()
ZStack {
ZStack {
Image(systemName: "sun.max.fill")
.foregroundColor(.yellow)
.opacity(configuration.isOn ? 1.0 : 0.0)
Image(systemName: "moon.fill")
.opacity(configuration.isOn ? 0.0 : 1.0)
}
.foregroundColor(Color.white)
.offset(x: configuration.isOn ? 10 : -10)
.frame(width: 20.0, height: 20.0)
.shadow(color: Color.black.opacity(0.1), radius: 3, x: configuration.isOn ? -2 : 2, y: 1)
}
.frame(width: 50.0, height: 30.0)
.background(background(configuration.isOn))
.clipShape(Capsule())
.offset(x: 2)
}
.onTapGesture {
withAnimation(Animation.spring(response: 0.3, dampingFraction: 0.7, blendDuration: 0)) {
configuration.isOn.toggle()
}
}
}
private func background(_ isOn: Bool) -> some View {
LinearGradient(
gradient: isOn ? lightGradient : darkGradient,
startPoint: .top,
endPoint: .bottom
)
.background(isOn ? Color.white : Color.black)
}private var lightGradient: Gradient {
Gradient(colors: [Color.blue.opacity(0.6), Color.blue.opacity(0.4)])
}private var darkGradient: Gradient {
Gradient(colors: [Color.blue.opacity(0.3), Color.blue.opacity(0.5)])
}
}
Change the style
.toggleStyle(DayNightToggleStyle())