I want to scale size of the Picker selection drop down consistently with the font size of the elements. I'm using code below to display a picker / drop down with list of symbols:
import SwiftUI
struct ContentView: View {
@State private var pickerSelection: String = "sun.min"
let symbols = ["sun.max.fill", "sun.min.fill", "sun.min", "figure.walk"]
var body: some View {
VStack {
Picker("Icon label", selection: $pickerSelection) {
ForEach(symbols, id: \.self) { symbolString in
Text(Image(systemName: symbolString))
}
}
.scaledToFit()
}
.padding()
}
}
I would like to increase size of the labels and make the picker labels, bigger, let's say I true to use .font(.title)
to achieve that:
struct ContentView: View {
@State private var pickerSelection: String = "sun.min"
let symbols = ["sun.max.fill", "sun.min.fill", "sun.min", "figure.walk"]
var body: some View {
VStack {
Picker("Icon label", selection: $pickerSelection) {
ForEach(symbols, id: \.self) { symbolString in
Text(Image(systemName: symbolString))
.font(.title)
}
}
.scaledToFit()
}
.padding()
}
}
How can I properly control the size of text/icon label within the Picker/drop-down element and expand it consistently with the size of the menu items?
I'm open to alternative UI elements that would enable me to achieve the same outcome in a presentable manner - drop down list with symbols, where I can control size of the whole element and scale accordingly with the size of the symbol.
You could try applying a .scaleEffect
to the Picker
.
By default, the Picker
fills the full width of the screen. To prevent this, apply .fixedSize()
or set a maxWidth
.
The scale effect applies to the picker label and the menu icon too. However, it doesn't seem to apply to the items seen in the expanded menu (unfortunately).
To keep the picker label at the regular size, supply an empty string to Picker.init
and use a leading Text
element instead. You might also like to add an .accessibilityLabel
to the Picker
, as substitute for the missing label.
For the picker items, I found it worked best to use a Label
with label style .iconOnly
, instead of wrapping the images as Text
items.
Use padding to compensate for the scaling having an impact on layout, if necessary.
VStack {
HStack {
Text("Icon label")
Picker("", selection: $pickerSelection) {
ForEach(symbols, id: \.self) { symbolString in
Label(symbolString, systemImage: symbolString)
.labelStyle(.iconOnly)
}
}
.accessibilityLabel("Icon label")
.fixedSize()
.scaleEffect(1.5)
.padding(.trailing, 14)
.padding(.vertical, 5)
}
}
.padding()
EDIT After a bit more experimentation, I found that you can get closer to a fully scaled result by using a Menu
instead of a Picker
:
Button
items.Button
is formed as an image wrapped as Text
, as used in your original example. This gives the larger appearance for the expanded menu that was missing from the previous solution above.ButtonStyle
. This gives you a lot of control over its appearance.I also tried the technique of showing the checkmark against the selected item by switching label style, as shown in part 2 of the answer to Section header inside a Menu for a Picker not visible (it was my answer). However, in this context, the checkmark is shown on the left, so this was causing the options to go out of alignment.
struct MyButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
HStack(spacing: 20) {
configuration.label
.scaleEffect(1.5)
.padding(.leading, 10)
Image(systemName: "chevron.up.chevron.down")
.resizable()
.scaledToFit()
.fontWeight(.bold)
.padding(3)
.frame(width: 20, height: 20)
.foregroundStyle(.white)
.background(
RoundedRectangle(cornerRadius: 4)
.fill(.blue)
)
}
.padding(4)
.background(
RoundedRectangle(cornerRadius: 6)
.fill(Color(NSColor.controlColor))
.shadow(radius: 2)
)
}
}
var body: some View {
HStack {
Text("Icon label")
Menu {
ForEach(symbols, id: \.self) { symbolString in
Button {
pickerSelection = symbolString
} label: {
Group {
if pickerSelection == symbolString {
Text(Image(systemName: symbolString)) +
Text(" ") +
Text(Image(systemName: "checkmark"))
} else {
Text(Image(systemName: symbolString))
}
}
.font(.title)
}
}
} label: {
Image(systemName: pickerSelection)
}
.accessibilityLabel("Icon label")
.buttonStyle(MyButtonStyle())
.fixedSize()
}
.padding(.bottom, 150)
}