Search code examples
iosswiftuipickerviewuicolor

Swift Picker Containing A View Not Showing The Color component


So I want to have a dropdown containing different categories and each category should have a color, the string value of the category should be binded to my viewmodel.category.

This is what I have:

View:

Picker("Category", selection: $viewModel.category) {
    ForEach(Category.allCases, id: \.self) { category in
        CategoryView(category: category)
            .tag(category.rawValue)
    }
}

Category View:

struct CategoryView: View {
    let category: Category

    var body: some View {
        HStack {
            
            Text(category.rawValue.capitalized)
            
            Circle()
                .fill(category.color)
                .frame(width: 20, height: 20)
                .padding(.trailing, 8)
        }
    }
}

Category Model:

enum Category: String, CaseIterable {
    case groceries
    case utilities
    case transportation
    case entertainment
    
    var color: Color {
        switch self {
        case .groceries:
            return Color.blue
        case .utilities:
            return Color.green
        case .transportation:
            return Color.orange
        case .entertainment:
            return Color.purple
        }
    }
}

The dropdown shows the categories just fine, but with no circle for color:

Screenshot

Also, if I put just a categoryview hardcoded in the view, like this:

CategoryView(category: .entertainment)

It shows up fine:

Screenshot

Any ideas?


Solution

  • The answer to SwiftUI Picker issue with Rectangle() instead of Text() shows how this can be done (it was my answer):

    • convert each Color to an Image
    • build the Picker using Labels that in turn are built using the name of the category and the color Image.

    Actually, it works to build the label using your CategoryView, but it is only the text part that gets used, so you might as well use the Text directly.

    Like this:

    private func colorImage(color: Color) -> Image {
        Image(size: CGSize(width: 26, height: 20)) { ctx in // includes trailing padding
            ctx.fill(
                Path(ellipseIn: CGRect(origin: .zero, size: CGSize(width: 20, height: 20))),
                with: .color(color)
            )
        }
    }
    
    
    var body: some View {
        Form {
            Picker("Category", selection: $viewModel.category) {
                ForEach(Category.allCases, id: \.self) { category in
                    Label(
                        title: { Text(category.rawValue.capitalized) },
                        icon: { colorImage(color: category.color) }
                    )
                    .tag(category.rawValue)
                }
            }
        }
    }
    

    Screenshot