Search code examples
swiftuiuiimagepicker

SwiftUI / Picker with custom Image and Text / Strange behavior


I am building a Form right know and want to implement a Picker. The Picker shall have a normal Image from my Assets plus the Text. In the selection, it works fine, but without selection, is's a mess:

enter image description here

Code below:

    
    @State private var name = ""
    @State private var careTypeName = "Prune"
    @State private var careDue = Date.now
    @State private var datePickerId: Int = 0
    @State private var selectedBonsai = "Juniperus"
    @State private var selectedBonsai2 = 0
    
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        NavigationView {
            Form {
                Section("Choose either a Taskname or pick Careoption") {
                    TextField("Taskname", text: $name)
                    Picker("Care option", selection: $careTypeName) {
                                ForEach(["Prune", "Watering", "Fertilize"], id: \.self) { subject in
                                  Text(subject)
                                }
                              }
                }
                Section("Pick a date for Task") {
                    DatePicker(selection: $careDue, in: ...Date.now, displayedComponents: .date) {
                        Text("Select a due date")
                    }
                    .id(datePickerId)
                    .onChange(of: careDue, perform: { _ in
                      datePickerId += 1
                    })
                    
                }
                Section("Select Bonsai:") {
                    Picker(selection: $selectedBonsai, label: Text("Bonsai")) {
                        HStack {
                            Image("d2")
                                .renderingMode(Image.TemplateRenderingMode.original)
                                .resizable()
                                .scaledToFill()
                                .frame(maxWidth: 60, maxHeight: 35)
                                .clipShape(RoundedRectangle(cornerRadius: 3))
                            
                            Text(selectedBonsai)
                            
                        }
                    }
                }
                
                Button {
                    dismiss()
                } label: {
                    HStack {
                        Spacer()
                        Text("Save Task")
                            .foregroundColor(Color("buttonColor"))
                        Spacer()
                    }
                }

            }
            .navigationBarTitleDisplayMode(.inline)
                    .toolbar {
                        ToolbarItem(placement: .topBarLeading) {
                            VStack {
                                Text("Add Task")
                                  .font(.system(size: 25))
                                  .bold()
                                  .foregroundColor(Color("titleColor"))
                            }.padding(.leading, 8)
                        }
                        ToolbarItem(placement: .topBarTrailing) {
                            Button(action: {
                                dismiss()
                            }, label: {
                                Text("Cancel")
                                    .foregroundColor(Color("headerButtons"))
                                    .bold()
                            })
                        }
                    }
            
        }
            
    }
}

Did I miss something? Where can I restrict the image-size in the "preview" from the Picker?

Thanks for help :)

If I'm try it with a systemName-Image it works as intended. enter image description here

Tried this as well: Image(uiImage: UIImage(imageLiteralResourceName: "d2"))

Thanks for help :)


Solution

  • Looking at the UI hierarchy, it appears that SwiftUI doesn't care about resizable or frame modifiers for images in the picker. It just translates the picker into a UIButton, with the image as the button's image, which is displayed using a UIImageView.

    This doesn't seem like something we can control. Perhaps we will be able to do that in a future version of SwiftUI.

    A simple solution is to just make a smaller version of the image, so that it fits into the list row.

    If you cannot do that for some reason, you can try to mimic how a .menu picker looks.

    LabeledContent("Bonsai") {
        HStack {
            Image("d2") // putting the image of the currently selected option outside of the picker
            // in reality you would do Image(f(selectedBonsai)) where f is a function
            // that converts the selected option to an image name
                .renderingMode(Image.TemplateRenderingMode.original)
                .resizable()
                .scaledToFit()
                .frame(maxWidth: 60, maxHeight: 35)
                .clipShape(RoundedRectangle(cornerRadius: 3))
            Menu {
                // put the picker inside a menu, so users see the images in the menu
                Picker(selection: $selectedBonsai) {
                    Label {
                        Text(selectedBonsai)
                    } icon: {
                        Image("my_image")
                    }
                    .tag("Juniperus")
                } label: { }
            } label: {
                HStack {
                    Text(selectedBonsai)
                    Image(systemName: "chevron.up.chevron.down")
                }
                .tint(.secondary)
                .imageScale(.small)
            }
        }
    }