0

描述:

我有一个具有以下层次结构的模型:

  • 食谱
  • ...步骤(一个数组)
  • ...当前步骤
  • ......参数(一个数组)
  • .........最低限度
  • .........最大
  • .........默认
  • .........当前

该模型运行良好。我可以添加步骤、参数,并将当前步骤设置为@EnvironmentObject被调用的recipe.

我在这里创建了一个示例项目,其中包含两个步骤和参数列表,以及三个按钮,用于在三个硬编码步骤中添加一个步骤,每个步骤包含一个 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)
                }
            }
        }
    }
}

同样,一个工作示例是 project here

4

1 回答 1

1

我还在浏览你的代码。但我想评论一下在您的函数 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() 不会执行?我建议您颠倒顺序并首先插入步骤,然后再更新 currentStep。这样,在您完成所有更改之后,您只需调用一次 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
            )
        }
    }
}
于 2019-07-12T15:47:43.197 回答