Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI dynamic List with @Binding controls

How do I build a dynamic list with @Binding-driven controls without having to reference the array manually? It seems obvious but using List or ForEach to iterate through the array give all sorts of strange errors.

struct OrderItem : Identifiable {
    let id = UUID()
    var label : String
    var value : Bool = false
}

struct ContentView: View {
    @State var items = [OrderItem(label: "Shirts"),
                        OrderItem(label: "Pants"),
                        OrderItem(label: "Socks")]
    var body: some View {
        NavigationView {
            Form {
                Section {
                    List {
                        Toggle(items[0].label, isOn: $items[0].value)
                        Toggle(items[1].label, isOn: $items[1].value)
                        Toggle(items[2].label, isOn: $items[2].value)
                    }
                }
            }.navigationBarTitle("Clothing")
        }
    }
}

This doesn't work:

            ...
                Section {
                    List($items, id: \.id) { item in
                        Toggle(item.label, isOn: item.value)
                    }
                }
            ...

Type '_' has no member 'id'

Nor does:

            ...
                Section {
                    List($items) { item in
                        Toggle(item.label, isOn: item.value)
                    }
                }
            ...

Generic parameter 'SelectionValue' could not be inferred

like image 553
joshd Avatar asked Sep 07 '19 19:09

joshd


2 Answers

Try something like

...
   Section {
       List(items.indices) { index in
           Toggle(self.items[index].label, isOn: self.$items[index].value)
       }
   }
...
like image 183
Maki Avatar answered Nov 01 '22 02:11

Maki


While Maki's answer works (in some cases). It is not optimal and it's discouraged by Apple. Instead, they proposed the following solution during WWDC 2021:

Simply pass a binding to your collection into the list, using the normal dollar sign operator, and SwiftUI will pass back a binding to each individual element within the closure.

Like this:

struct ContentView: View {
    @State var items = [OrderItem(label: "Shirts"),
                        OrderItem(label: "Pants"),
                        OrderItem(label: "Socks")]

    var body: some View {
        NavigationView {
            Form {
                Section {
                    List($items) { $item in
                        Toggle(item.label, isOn: $item.value)
                    }
                }
            }.navigationBarTitle("Clothing")
        }
    }
}
like image 7
Damiaan Dufaux Avatar answered Nov 01 '22 02:11

Damiaan Dufaux