Is it possible to style a Picker of .pickerStyle(.menu) ? I want to change color of up-down arrows on the picker button and background color behind them.
-
1Please edit to focus on one thing you want to customise, especially not "etc".Sweeper– Sweeper2025-05-22 10:48:27 +00:00Commented May 22 at 10:48
-
OK, edited to be focused on just a single issue.Kaven– Kaven2025-05-22 12:25:59 +00:00Commented May 22 at 12:25
Add a comment
|
1 Answer
To change the styling of the picker button you can wrap the picker with a Menu. Then:
- change the style of the wrapped picker to
.inline - apply
.labelsHidden(), to prevent the picker label from being shown - use your own
ButtonStyleto style the menu button.
The label of the menu button can show the current selection. However, if the choices have different widths then the button width will keep changing.
- The simplest way to prevent different labels from causing the button width to change is to set a fixed minimum width on the label part.
- Alternatively, the minimum width can be determined dynamically by showing all the labels in a
ZStack. This can then be hidden and used as the footprint for the actual visible label, which is shown as an overlay.
Here is an example to show it all working. The styled button tries to emulate the appearance of the native picker, but with a different icon:
struct ContentView: View {
let choices = ["Zero", "One", "Two", "Three", "Four", "Five"]
@State private var selection = "Zero"
var body: some View {
VStack(alignment: .trailing) {
Picker("Native picker", selection: $selection) {
ForEach(choices, id: \.self) { choice in
Text(choice).tag(choice)
}
}
.pickerStyle(.menu)
.fixedSize()
LabeledContent("Styled picker") {
Menu(selection) {
Picker("", selection: $selection) {
ForEach(choices, id: \.self) { choice in
Text(choice).tag(choice)
}
}
.pickerStyle(.inline)
.labelsHidden()
}
.buttonStyle(MenuButton(choices: choices))
}
.fixedSize()
}
}
}
struct MenuButton: ButtonStyle {
let choices: [String]
func makeBody(configuration: Configuration) -> some View {
HStack {
footprint
.overlay(alignment: .leading) {
configuration.label
}
.padding(.leading, 6)
Image(systemName: "atom")
.imageScale(.small)
.padding(1)
.foregroundStyle(.white)
.background(.purple, in: .rect(cornerRadius: 4))
}
.padding(2)
.background {
RoundedRectangle(cornerRadius: 5)
.fill(.background)
.shadow(radius: 1, y: 0.5)
}
}
private var footprint: some View {
ZStack {
ForEach(choices, id: \.self) { choice in
Text(choice)
}
}
.hidden()
}
}

1 Comment
Kaven
Good idea ... and works!