描述:
我有一个具有以下层次结构的模型:
- 食谱
- 。步骤(数组)
- 。当前步骤
- ......参数(数组)
- .........最低
- .........最大
- .........违约
- .........当前
该模型运行良好。我可以添加步骤、参数,并将当前步骤设置为名为recipe
的@EnvironmentObject
。
我在这里创建了一个示例项目,其中包含两个步骤和参数列表,以及三个按钮,用于在三个硬编码步骤中添加单个步骤,每个步骤都包含一个 0、1 或 3 个参数的数组。
顶部列表是步骤行,每个行都是填充底部列表的按钮。底部列表是参数列表,每个参数列表都包含一个标签和一个VStack
滑块。
一切正常,除了我 (a) 将滑块绑定到我的模型和(b) 列表包含比当前步骤现在更多的滑块(行)。我得到一个index out of range error
.
如果我将滑块值绑定到局部变量,它都可以工作。以下是相关代码:
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)
}
}
}
}
}
同样,这方面的一个工作示例是这里的项目。
我仍在浏览您的代码。但我想评论一下在您的函数 addStepX() 中引起我注意的东西:
func addStep1() {
let newStep = Step(id: UUID(), name: "Step #1", parameters: [Parameter]())
currentStep = newStep
steps.insert(newStep, at: steps.count)
}
您是否知道 steps.insert() 不会触发 didSet,因此 didChange.send() 不会执行?我建议你颠倒顺序,先插入步骤,然后再更新当前步骤。这样,在所有更改完成后,您只调用一次didChange.send()。
func addStep1() {
let newStep = Step(id: UUID(), name: "Step #1", parameters: [Parameter]())
steps.insert(newStep, at: steps.count)
currentStep = newStep
}
请注意,更改它仍然不能解决问题,但我认为我应该提到,因为这绝对是一个问题。
更新
更改后,代码看起来更干净。而且我似乎找到了一种防止越界的方法。
问题似乎是由于时间问题造成的。您的数组已更新,但 List 传递的参数仍然旧。最终,它会追赶,但由于越界崩溃......它永远不会。那么为什么不让它有条件呢?
请注意,我还向 Text() 视图添加了滑块值,以表明绑定成功:
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) = (parameter.id < recipe.currentStep.parameters.count ? recipe.currentStep.parameters[parameter.id].current : -1)")
Slider(
value: parameter.id < recipe.currentStep.parameters.count ? $recipe.currentStep.parameters[parameter.id].current : .constant(0),
from: parameter.minimum,
through: parameter.maximum,
by: 1.0
)
}
}
}