I have an array of Strings, "categories", and associated to each category I have an "Item" struct in another array. With these I have formed a dictionary of type [String: [Item]]
.
I want to pass the item array from the dictionary as a Binding
to ListRow
so that changes are observed by the ContentView
.
Xcode shows me this error:
Referencing initializer 'init(_:content:)' on 'ForEach' requires that 'Item' conform to 'Identifiable
.
The solution from this question, Iterating through set with ForEach in SwiftUI, does not use any @State
or @Published
variables. They are just using them for showing the data. Any workaround for this issue?
struct Item {
var id = UUID().uuidString
var name: String
}
struct ListRow {
@Binding var item: Item
var body: some View {
TextField("Place Holder", text: $item.name)
}
}
struct ContentView: View {
var categories = ["Bakery","Fruits & Vagetables", "Meat & poultry", "Dairy & Eggs", "Pantry", "Household"]
@State private var testDictionary: [String: [Item]] = [:]
var body: some View {
VStack(alignment: .leading) {
ForEach(categories, id: \.self) { category in
Text(category)
.font(.system(size: 30))
ForEach(testDictionary[category]) { item in
ListRow(item: item)
}
}
}.onAppear(
addDummyDateIntoDictonary()
)
}
func addDummyDateIntoDictonary() {
for category in categories {
testDictionary[category] = [Item(name: category + "1"), Item(name: category + "2")]
}
}
}
One problem is that you didn't make ListRow
conform to View
.
// add this
// ╭─┴──╮
struct ListRow: View {
@Binding var item: Item
var body: some View {
TextField("Place Holder", text: $item.name)
}
}
Now let's address your main problem.
A Binding
is two-way: SwiftUI can use it to get a value, and SwiftUI can use it to modify a value. In your case, you need a Binding
that updates an Item
stored somewhere in testDictionary
.
You can create such a Binding
“by hand” using Binding.init(get:set:)
inside the inner ForEach
.
struct ContentView: View {
var categories = ["Bakery","Fruits & Vagetables", "Meat & poultry", "Dairy & Eggs", "Pantry", "Household"]
@State private var testDictionary: [String: [Item]] = [:]
var body: some View {
VStack(alignment: .leading) {
ForEach(categories, id: \.self) { category in
Text(category)
.font(.system(size: 30))
let items = testDictionary[category] ?? []
ForEach(items, id: \.id) { item in
let itemBinding = Binding<Item>(
get: { item },
set: {
if
let items = testDictionary[category],
let i = items.firstIndex(where: { $0.id == item.id })
{
testDictionary[category]?[i] = $0
}
}
)
ListRow(item: itemBinding)
}
}
}.onAppear {
addDummyDateIntoDictonary()
}
}
func addDummyDateIntoDictonary() {
for category in categories {
testDictionary[category] = [Item(name: category + "1"), Item(name: category + "2")]
}
}
}