I'm trying to create what I thought would be a simple bit of code to show the key of a dictionary item and implement a Stepper or a Picker to modify the key's value in a single line within a Form view.
At the end of the day, all I'm trying to accomplish is showing the user a list of words they've entered, along with an associated number value that they can change.
Almost every method to solution my problem resulted in absolutely blowing up (crashing) my canvas because evidently my attempts at solutioning this are just that wrong.
Anyway, below are some examples of ways I've been trying to solve my problem. Note that I've also tried many types of views, like Text with the word, and a picker for the associated Integer. I just figure for the sake of demonstrating the problem, using a Stepper for each example might be good enough.
struct Likenesses {
let word: String
var likeness: Int = 0
}
struct ContentView: View {
@State private var words: [String] = []
@State private var likeness: [Int] = []
@State private var wordLikeness: [String:Int] = [:]
@State private var StructLikeness: [Likenesses] = []
var body: some View {
Form {
// Dict Method
Section {
ForEach(Array(wordLikeness.keys), id: \.self) { key in
Stepper("\(key)", value: $wordLikeness[key], in: 0...8, step: 1)
}
}
// Arrays method
Section {
ForEach(0...words.count, id: \.self) { i in
Stepper("\(word[i])", value: $likeness[i], in: 0...8, step: 1)
}
}
// Struct method
Section {
ForEach(StructLikeness, id: \.self) { s in
/*
Yes, I know structs are not references and pass copies around, this was another issue I was going to have to solve eventually, but for now, this gets the idea of what I was trying to solve with a struct
*/
Stepper("\(s.word)", value: $s.likeness, in: 0...8, step: 1)
}
}
}
}
}
Dictionary elements don't have a particular order, so you need to decide on an order first, before you use them in ForEach
. For example, if you want to sort by the keys,
@State private var likenesses: [String: Int] = ["Foo": 3]
Form {
Section {
ForEach(likenesses.keys.sorted(), id: \.self) { key in
Stepper("\(key): \(likenesses[key]!)", value: Binding($likenesses[key])!, in: 0...8, step: 1)
}
}
}
I would prefer passing an array of structs instead. Create the binding when creating the ForEach
, not when creating the steppers.
@State private var likenesses: [Likenesses] = [...]
Form {
Section {
ForEach($likenesses) { s in
Stepper("\(s.wrappedValue.word): \(s.wrappedValue.likeness)", value: s.likeness, in: 0...8, step: 1)
}
}
}
Note that the struct should conform to Identifiable
:
struct Likenesses: Identifiable {
let word: String
var likeness: Int = 0
// here I'm using word as the id
var id: String { word }
}