Setup SwiftUI Picker with a label
Have you ever wondered where the label of Picker with PickerStyle .inline disappeared to and never came back?
Even with a label parameter no label as initial value is shown, instead of that only a chevron shows up.
Picker(selection: $gender) {
Text(Gender.male.rawValue).tag(Gender.male)
Text(Gender.female.rawValue).tag(Gender.female)
Text(Gender.nonbinary.rawValue).tag(Gender.nonbinary)
} label: {
Text("Select your Gender")
.font(.largeTitle)
}
.accentColor(.white)
Let’s make it look better
I changed the accentColor to better match the design. The original standard blue chevrons now fit perfectly.
.accentColor(.pink)
When the initial label value is displayed, I must shift the chevron to the right slightly.
.offset(x: gender == .unselected ? 90 : 0)
Then I set the frame and padding, depending on whether the initial label value is shown.
.frame(width: 200, height: 40)
.padding(.horizontal, gender == .unselected ? 10 : 0)
Finally, I added a background to the Picker. Since a value from the Picker is shown when selected, we had to address that here.
.background {
Capsule(style: .continuous)
.fill(.white)
if gender == .unselected {
Text(gender.rawValue)
}
}
There are, in fact, some cave sheets.
Firstly, I don’t like the code with the background and the shifting position of the chevron. It may seem simple, but in more complex settings and with different device support, it causes a lot of problems.
Secondly, the menu can only be opened by clicking on the chevron, not the area in the button-like design. This is an unacceptable UI solution.
Let’s do it better
If one want a menu, one should use a menu.
So I’m using the Menu View.
Menu(content: <#T##() -> View#>, label: <#T##() -> View#>)
The inner Menu part is straight forward,
Menu {
Picker("", selection: $gender,
content: {
Text(Gender.male.rawValue).tag(Gender.male)
Text(Gender.female.rawValue).tag(Gender.female)
Text(Gender.nonbinary.rawValue).tag(Gender.nonbinary)
})
.pickerStyle(.inline)
.font(.largeTitle)
.accentColor(.white)
}
The label part of Menu can be designed with all more or less all meaningfull SwiftUI Elements.
Menu {
Picker("", selection: $gender,
content: {
ForEach(Gender.allCases) { data in
Text(data.rawValue).tag(data)
}
})
.pickerStyle(.inline)
.font(.largeTitle)
.accentColor(.white)
}
label: {
HStack {
Text(gender.rawValue)
.font(.title3)
.frame(maxWidth: 200)
Image(systemName: "chevron.up.chevron.down")
.padding(.trailing)
}
.frame(maxWidth: .infinity)
.frame(height: 55)
.background(.white)
.cornerRadius(10)
.accentColor(.pink)
}
The complete button-styled area is now selectable and can be easily styled without moving the standard chevron. The menu label is used here instead of the label of the picker.