bcause
bcause

Reputation: 1313

How can I extend Picker with a custom initializer?

I am trying to get the following code to work:

extension Picker where Label == Text, SelectionValue == Bool {
    init(selection: Binding<SelectionValue>) {
        self.init("Bool", selection: selection) {
            Text("TRUE").tag(true)
            Text("FALSE").tag(false)
        }
    }
}

But get this compiler error:

Cannot convert value of type 'TupleView<(some View, some View)>' to closure result type 'Content'

The goal is to be able to create a picker the following way:

@State private var value = false

var body: some View {
    Form {
        Picker($value)
    }
}

I understand that the compiler tries to infer Content from the caller of the initializer, but since I am calling an existing initializer and provide it with content, why am I still seeing this error?

Upvotes: 1

Views: 315

Answers (1)

Sweeper
Sweeper

Reputation: 271660

You specified what the Label and SelectedValue types for the Picker should be in the extension header, but not Content, so this initialiser is added to pickers with all kinds of content types. But you don't really want that, do you? This initialiser only produces pickers with this specific type of Content:

{
    Text("TRUE").tag(true)
    Text("FALSE").tag(false)
}

But we can't really name the type of that, because it's TupleView<(some View, some View)>, and opaque types can't be used like that:

// nope!
extension Picker where Label == Text, SelectionValue == Bool, Content == TupleView<(some View, some View)> {

One way to work around this is to wrap the Texts in AnyViews:

extension Picker where Label == Text, SelectionValue == Bool, Content == TupleView<(AnyView, AnyView)> {
    init(_ selection: Binding<SelectionValue>) {
        self.init("Bool", selection: selection) {
            AnyView(Text("TRUE").tag(true))
            AnyView(Text("FALSE").tag(false))
        }
    }
}

Upvotes: 2

Related Questions