1

I'm curious, is there a way to affect the layout of labels used to build the button that invokes the selection menu for a SwiftUI picker? I'm dissatisfied with the results I'm getting because:

a) The padding between symbol and text seems insufficient.

b) The alignment of symbols between consecutive pickers is ugly.

Picker Example

In the above example image, I'd like the symbols to be a bit more distant from the Circle and Rectangle text. And I'd like the Circle and the Rectangle to be centered to each other (ie look like they are in the same column).

The image comes from the following code:

struct PickerLayout: View {
    @State private var first = "circle"
    @State private var second = "rectangle"

    var body: some View {
        Form {
            Picker("First Shape", selection: $first) {
                Label("Circle", systemImage: "circle").tag("circle")
                Label("Rectangle", systemImage: "rectangle").tag("rectangle")
            }
            Picker("Second Shape", selection: $second) {
                Label("Circle", systemImage: "circle").tag("circle")
                Label("Rectangle", systemImage: "rectangle").tag("rectangle")
            }
        }
    }
}
0

1 Answer 1

2

You can do a similar transformation to my answer here, to transform the Picker into a structure like this:

LabeledContent("Some Label") { // move the picker's label to here
    Menu {
        Picker(selection: ...) {
            ...
        } label: { }
    } label: {
        HStack {
            Image(...) // the picked image
            Text(...) // the label for the picked image
            Image(systemName: "chevron.up.chevron.down")
                .imageScale(.small)
        }
    }
}

This way, you have greater control over the menu's label. You can freely adjust the whitespace between the picked image and the text (the HStack already adds some spacing by default). You can also align the picked images by setting a fixed width for the HStack and adding some spacers.

Here is a full example:

struct ContentView: View {
    @State var firstShape = ShapeOption.circle
    @State var secondShape = ShapeOption.rectangle
    
    public var body: some View {
        Form {
            MyPicker(label: "First Shape", selection: $firstShape) {
                pickerContent
            } pickedImage: { shape in
                Image(systemName: shape.imageName)
            } pickedImageLabel: { shape in
                Text(shape.displayName)
            }

            MyPicker(label: "Second Shape", selection: $secondShape) {
                pickerContent
            } pickedImage: { shape in
                Image(systemName: shape.imageName)
            } pickedImageLabel: { shape in
                Text(shape.displayName)
            }
            
        }
    }
    
    var pickerContent: some View {
        ForEach([ShapeOption.circle, .rectangle], id: \.self) { shape in
            Label {
                Text(shape.displayName)
            } icon: {
                Image(systemName: shape.imageName)
            }
        }
    }
}

struct MyPicker<Content: View, Value: Hashable>: View {
    let label: String
    @Binding var selection: Value
    @ViewBuilder let pickerContent: () -> Content
    let pickedImage: (Value) -> Image
    let pickedImageLabel: (Value) -> Text
    
    var body: some View {
        LabeledContent(label) {
            Menu {
                Picker(selection: $selection) {
                    pickerContent()
                } label: { }
            } label: {
                HStack {
                    pickedImage(selection)
                    Spacer()
                    pickedImageLabel(selection)
                    Image(systemName: "chevron.up.chevron.down")
                        .imageScale(.small)
                }
                .frame(width: 140)
                .tint(.secondary)
            }
        }
    }
}

struct ShapeOption: Hashable {
    let displayName: String
    let imageName: String
    
    static let circle = ShapeOption(displayName: "Circle", imageName: "circle")
    static let rectangle = ShapeOption(displayName: "Rectangle", imageName: "rectangle")
}

Output:

enter image description here

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.