How to hide the label of a Picker, Stepper, Toggle, and more using labelsHidden()
How to hide the label of a Picker, Stepper, Toggle, and more using labelsHidden() êŽë š
Updated for Xcode 15
SwiftUI requires that we add labels to its controls, and it's common to want to hide those labels so you can get a more precise UI layout. However, there's a bad way of hiding labels and a good way, and it's already common to see folks choosing the bad choice despite it actively hurting users.
First, let's look at the * * way to hide labels. As an example, here's a Picker that lets users select a number:
struct ContentView: View {
@State private var selectedNumber = 0
var body: some View {
Picker("Select a number", selection: $selectedNumber) {
ForEach(0..<10) {
Text("\($0)")
}
}
}
}
On some platforms, that will show âSelect a numberâ alongside the picker. This can look poor, because the label is sometimes crammed into a small space â a better idea would be to have a VStack
with a custom text label, then hide the label in the Picker
.
To hide the label for a Picker
â or indeed for a DatePicker
, a Stepper
, a Toggle
, or any other view that requires a label â you should add the labelsHidden()
modifier to the view, like this:
struct ContentView: View {
@State private var selectedNumber = 0
var body: some View {
Picker("Select a number", selection: $selectedNumber) {
ForEach(0..<10) {
Text("\($0)")
}
}
.labelsHidden()
}
}
That still creates the label, but now it doesn't get shown no matter what platform is used. As a result, you can put the Picker
inside a VStack
and add a label of your own choosing.
Tips
If you want all your labels hidden you can apply the labelsHidden()
modifier to a VStack
or whatever you're using as your outermost container.
In case you were curious, the wrong way to hide labels is using EmptyView
, like this:
struct ContentView: View {
@State private var selectedNumber = 0
var body: some View {
Picker(selection: $selectedNumber, label: EmptyView()) {
ForEach(0..<10) {
Text("\($0)")
}
}
}
}
Yes, the end result might look the same, but there's an important reason why using labelsHidden()
is much better: the hidden label is still accessible by the screen reader, so even though the text isn't visible it's still there to help VoiceOver understand how your UI is structured.