0

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?

1
  • Youll need a custom picker Commented Mar 28, 2024 at 16:43

1 Answer 1

0

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

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.