How to use a Toggle to create a Switch in SwiftUI | SwiftUI Bootcamp #37
Settings screens, notification preferences, feature flags — whenever users need to turn something on or off, a Toggle is the right control. This lesson shows you how to wire a Toggle to a @State Bool, use SwitchToggleStyle with a custom tint color, and display reactive text that reflects the toggle's state.
What You'll Learn
- How
Toggle(isOn:label:)binds to a@StateBool and renders a native iOS switch - How
SwitchToggleStyle(tint:)customizes the active color of the toggle - How to read the toggle's state to reactively update other parts of your UI
Mental Model
A Toggle in SwiftUI is a two-state light switch wired directly to your @State Bool. The switch is the visual control; your Bool is the electrical circuit. When the switch flips on, the Bool becomes true. When it flips off, the Bool becomes false. Any view in your hierarchy that reads this Bool will immediately reflect the change.
This is the same @State ↔ view binding you've used with TextField and Picker, but for a Boolean. The isOn: $toggleIsOn parameter is the two-way binding — the Toggle reads the current value to show the correct position, and writes the new value when the user flips it.
Detailed Explanation
Toggle(isOn: $toggleIsOn) { label } creates the standard iOS switch control. The label view (the trailing closure) appears to the left of the switch. It can be any view — commonly a Text, but it could be an HStack with an icon.
$toggleIsOn is a Binding to a Bool. Passing a binding lets the Toggle write back to the state it doesn't own. Without the $, you'd pass just the current Bool value and the Toggle would have no way to flip it.
.toggleStyle(SwitchToggleStyle(tint: Color(...))) customizes the accent color shown when the toggle is in the ON state. Without this, the toggle uses the system blue accent color. SwitchToggleStyle is the default style; you only need to specify it explicitly when you want to change the tint.
Reactive text driven by the toggle's state is demonstrated with Text(toggleIsOn ? "Online" : "Offline"). This ternary is evaluated every time body renders. Because toggleIsOn is @State, flipping the toggle causes body to re-run, the ternary re-evaluates, and the text updates — all automatically.
.padding(.horizontal, 100) on the VStack narrows the container, which makes the Toggle shorter and more natural-looking for a settings-style layout.
Code Structure
ToggleBootcamp.swift displays a status HStack (showing "Online"/"Offline" based on the toggle) and a labeled Toggle with a custom purple tint. The layout uses horizontal padding to create a compact, settings-like appearance. There are no helper functions — all logic is in the ternary expressions inline.
Complete Code
ToggleBootcamp.swift
import SwiftUI
struct ToggleBootcamp: View {
@State var toggleIsOn: Bool = false // source of truth for the toggle's on/off state
var body: some View {
VStack {
HStack {
Text("Status:")
Text(toggleIsOn ? "Online" : "Offline") // reacts to the toggle — no extra code needed
}
.font(.title)
Toggle(
isOn: $toggleIsOn, // two-way binding: reads current state, writes new state on flip
label: {
Text("Change status") // label appears to the left of the switch
})
.toggleStyle(SwitchToggleStyle(tint: Color(#colorLiteral(red: 0.5818830132, green: 0.2156915367, blue: 1, alpha: 1)))) // custom purple tint when ON
Spacer()
}
.padding(.horizontal, 100) // narrows the layout to a settings-column width
}
}
struct ToggleBootcamp_Previews: PreviewProvider {
static var previews: some View {
ToggleBootcamp()
}
}Code Walkthrough
@State var toggleIsOn: Bool = false— The single source of truth. TheTogglereads it to know which position to display (on or off). TheHStacktext reads it to know which label to show. Both update automatically when the Bool changes.Text(toggleIsOn ? "Online" : "Offline")— A ternary that readstoggleIsOnfrom@State. SwiftUI re-renders thisTextwhenevertoggleIsOnchanges. The dependency is implicit — no.onChangeor explicit subscription needed.Toggle(isOn: $toggleIsOn, label: { Text("Change status") })— TheisOn:parameter accepts aBindingto aBool. The label closure provides the descriptive text that appears to the left of the switch. In iOS 16+, there's a shorthand initializer:Toggle("Change status", isOn: $toggleIsOn)..toggleStyle(SwitchToggleStyle(tint: Color(...)))— Thecolor literalhere is a purple color. When the toggle is ON, the switch track appears in this purple instead of the default system blue. When the toggle is OFF, the track is the system gray regardless of tint..padding(.horizontal, 100)— Applied to theVStack, this pulls in both horizontal edges by 100 points. It creates the narrower column layout typical of settings screens. Without it, theTogglewould stretch to the full width of the screen.Spacer()at the bottom — Pushes all content to the top. Without it, theVStackwould center its contents vertically — fine for some layouts but not for this settings-style screen.
Common Mistakes
Mistake: Passing toggleIsOn instead of $toggleIsOn to isOn:
Without $, the Toggle receives a copy of the Bool's current value but has no way to write back. The switch will flip visually for a moment then spring back to its original position because the underlying state never changed.
Mistake: Adding .onChange(of: toggleIsOn) just to read the new value when it seems simpler
In many cases, you don't need .onChange at all. If your reactive text uses a ternary on toggleIsOn directly in body, SwiftUI handles the update automatically. Use .onChange only when you need to trigger a side effect (like saving to UserDefaults or making a network call) when the toggle changes.
Mistake: Using SwitchToggleStyle without the tint: parameter and expecting the default blueSwitchToggleStyle() with no tint: argument uses the default system accent color, which is blue unless the app has a custom accent color set. This is perfectly fine — you only need tint: when you want a specific non-default color.
Key Takeaways
Toggleis a two-way binding to aBool: flipping the switch updates the Bool, and any view reading the Bool re-renders immediately.SwitchToggleStyle(tint:)customizes the ON-state color — the only customization you can make with the built-in toggle style.- You rarely need
.onChangefor simple reactive text updates — ternary expressions inbodyhandle this automatically through SwiftUI's state observation.
Last updated: June 27, 2026