Description:
I have a model that has the following hierarchy:
- Recipe
- ...steps (an array)
- ...currentStep
- ......parameters (an array)
- .........minimum
- .........maximum
- .........default
- .........current
The model works well. I can add steps, parameters, and set the current step to an @EnvironmentObject
called recipe
.
I've created a sample project here with I two lists of steps and parameters, along with three buttons to add a single step among three hard-coded ones, each containing an array of 0, 1, or 3 parameters.
The top list is the step rows, each being a button to populate the bottom list. The bottom list is the parameter list, each containing a label and a slider in a VStack
.
All works fine, except when i am (a) binding the slider to my model and (b) the list contains more sliders (row) than the current step now has. I get an index out of range error
.
If I bind the slider value to a local variable, it all works. Here's the relevant code:
class Recipe: BindableObject {
var didChange = PassthroughSubject<Void, Never>()
var currentStep = Step() {
didSet {
didChange.send(())
}
}
}
struct Parameter: Identifiable {
var id:Int = 0
var name = ""
var minimum:Float = 0
var maximum:Float = 100
var `default`:Float = 30
var current:Float = 30
}
struct StepRow: View {
@EnvironmentObject var recipe: Recipe
var step: Step!
init(step: Step) {
self.step = step
}
var body: some View {
Button(action: {
self.setCurrentStep()
}) {
HStack {
Text(step.name).font(Font.body.weight(.bold))
}.frame(height: 50)
}
}
func setCurrentStep() {
recipe.currentStep = step
}
}
struct ParameterRow: View {
@EnvironmentObject var recipe: Recipe
@State var sliderValue:Float = 30
var parameter: Parameter!
init(parameter: Parameter) {
self.parameter = parameter
}
var body: some View {
VStack {
Text(parameter.name)
Slider(
// This works, swap these two lines to duplicate the index out of range error by:
// - Adding step 1, step 2, step 3, and finally step 4
// - Tapping each step in the step list in order, the first three will work but the last one won't
//value: $recipe.currentStep.parameters[parameter.id].current,
value: self.$sliderValue,
from: parameter.minimum,
through: parameter.maximum,
by: 1.0
)
}
}
}
struct ContentView : View {
@EnvironmentObject var recipe: Recipe
var body: some View {
VStack {
List {
ForEach(recipe.steps) { step in
StepRow(step: step)
}
}
List {
ForEach(recipe.currentStep.parameters) { parameter in
ParameterRow(parameter: parameter)
}
}
}
}
}
Again, a working example of this is project here.
var `default`: String
. Then you can use the variable without the quotes. – ArchducalCoreImage
app and that's the more correct way to use it. – Macdougall