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.
To change the styling of the picker button you can wrap the picker with a Menu
. Then:
.inline
.labelsHidden()
, to prevent the picker label from being shownButtonStyle
to 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.
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()
}
}