Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI Picker in Form does not show checkmark

Tags:

ios

swiftui

I have a Picker embedded in Form, however I can't get it working that it shows a checkmark and the selected value in the form.

NavigationView {
    Form {
        Section {
                Picker(selection: $currencyCode, label: Text("Currency")) {
                    ForEach(0 ..< codes.count) {
                        Text(self.codes[$0]).tag($0)
                    }
                }
            }
    }
}

No checkmark and no selected value

like image 935
gpichler Avatar asked Sep 25 '19 17:09

gpichler


People also ask

What is a picker in SwiftUI?

Picker is a control in SwiftUI which allows you to select a value from a list of possible options. In order to properly use a Picker, you need to back it with an array of possible options to choose from and a State variable storing the index of selected option in the array.

Is there a way to make a checkbox in SwiftUI?

There’s lots of good stuff in SwiftUI. One missing control is old fashioned checkbox like I have on the web or on my Mac. There is a way to make a checkbox with a Toggle control. However, as a bit of a intro to the philosophy of SwiftUI, let look at creating a checkbox.

How to get sfsymbol from SwiftUI?

I can use the Image object in SwiftUI with the systemName parameter to get a SFSymbol from SwiftUI. The checked square is simply the word checkmark before the square. I just have to switch between them. That will need some kind of variable.

How do I use the picker in a from view?

By wrapping the Picker in a From, we’re giving it extra functionality – the picker now becomes a single horizontal row with selected value in it. Tapping on the picker navigates away to a new view which contains a list of possible options. Tapping a desired option, selects it and navigates back to the original view.


2 Answers

TL;DR

Your variable currencyCode does not match the type of the ID for each element in your ForEach. Either iterate over the codes in your ForEach, or pass your Picker an index.

Below are three equivalent examples. Notice that the @State variable which is passed to Picker always matches the ID of element that the ForEach iterates over:

Also note that I have picked a default value for the @State variable which is not in the array ("", -1, UUID()), so nothing is shown when the form loads. If you want a default option, just make that the default value for your @State variable.

Example 1: Iterate over codes (i.e. String)

struct ContentView: View {
    @State private var currencyCode: String = ""
    var codes: [String] = ["EUR", "GBP", "USD"]

    var body: some View {
        NavigationView {
            Form {
                Section {
                    Picker(selection: $currencyCode, label: Text("Currency")) {
                        // ID is a String ----v
                        ForEach(codes, id: \.self) { (string: String) in
                            Text(string)
                        }
                    }
                }
            }
        }
    }
}

Example 2: Iterate over indices (i.e. Int)

struct ContentView: View {
    @State private var selectedIndex: Int = -1
    var codes: [String] = ["EUR", "GBP", "USD"]

    var body: some View {
        NavigationView {
            Form {
                Section {
                    Picker(selection: $selectedIndex, label: Text("Currency")) {
                        // ID is an Int --------------v
                        ForEach(codes.indices, id: \.self) { (index: Int) in
                            Text(self.codes[index])
                        }
                    }
                }
            }
        }
    }
}

Example 3: Iterate over an identifiable struct by its ID type (i.e. UUID)

struct Code: Identifiable {
    var id = UUID()
    var value: String

    init(_ value: String) {
        self.value = value
    }
}

struct ContentView: View {
    @State private var selectedUUID = UUID()
    var codes = [Code("EUR"), Code("GBP"), Code("USD")]

    var body: some View {
        NavigationView {
            Form {
                Section {
                    Picker(selection: $selectedUUID, label: Text("Currency")) {
                        // ID is a UUID, because Code conforms to Identifiable
                        ForEach(self.codes) { (code: Code) in
                            Text(code.value)
                        }
                    }
                }
            }
        }
    }
}
like image 72
John M. Avatar answered Sep 23 '22 22:09

John M.


It's difficult to say, what you are doing wrong, because your example doesn't include the declaration of codes or currencyCode. I suspect that the problem is with the binding being of a different type than the tag you are setting on a picker (which is an Int in your case).

Anyway, this works:

struct ContentView: View {

    let codes = Array(CurrencyCode.allCases)

    @State private var currencyCode: CurrencyCode?


    var body: some View {
        NavigationView {
            Form {
                Section {
                    Picker("Currency",
                           selection: $currencyCode) {
                            ForEach(codes, id: \.rawValue) {
                                Text($0.rawValue).tag(Optional<CurrencyCode>.some($0))
                            }
                    }
                }
            }
        }

    }
}


enum CurrencyCode: String, CaseIterable {

    case eur = "EUR"
    case gbp = "GBP"
    case usd = "USD"
}
like image 38
LuLuGaGa Avatar answered Sep 23 '22 22:09

LuLuGaGa