Swiftui 将滑块值绑定到EnvironmentObject中的嵌套数组时索引超出范围
说明: 我有一个具有以下层次结构的模型:Swiftui 将滑块值绑定到EnvironmentObject中的嵌套数组时索引超出范围,swiftui,combine,Swiftui,Combine,说明: 我有一个具有以下层次结构的模型: 配方 …步骤(数组) …currentStep ……参数(数组) 最低限度 最大值 ……违约 当前 该模型运行良好。我可以添加步骤、参数,并将当前步骤设置为名为配方的@EnvironmentObject 我创建了一个示例项目,其中包含两个步骤和参数列表,以及三个按钮,用于在三个硬编码步骤中添加一个步骤,每个步骤包含一个0、1或3个参数的数组 顶部列表是步骤行,每个行都是填充底部列表的按钮。底部列表是参数列表,每个参数列表包含VStack中的标签和滑块
- 配方
- …步骤(数组)
- …currentStep
- ……参数(数组)
- 最低限度
- 最大值
- ……违约
- 当前
配方的@EnvironmentObject
我创建了一个示例项目,其中包含两个步骤和参数列表,以及三个按钮,用于在三个硬编码步骤中添加一个步骤,每个步骤包含一个0、1或3个参数的数组
顶部列表是步骤行,每个行都是填充底部列表的按钮。底部列表是参数列表,每个参数列表包含VStack
中的标签和滑块
除了(a)将滑块绑定到我的模型和(b)列表中包含的滑块(行)比当前步骤中包含的滑块(行)更多时,所有操作都正常。我得到一个索引超出范围错误
如果我将滑块值绑定到一个局部变量,那么一切都可以正常工作。以下是相关代码:
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)
}
}
}
}
}
类配方:BindableObject{
var didChange=PassthroughSubject()
var currentStep=步骤(){
迪塞特{
didChange.send(())
}
}
}
结构参数:可识别{
变量id:Int=0
var name=“”
变量最小值:浮点=0
var最大值:浮动=100
var`default`:Float=30
无功电流:浮动=30
}
结构StepRow:视图{
@环境对象变量配方:配方
步骤:步骤!
初始化(步骤:步骤){
self.step=step
}
var body:一些观点{
按钮(操作:{
self.setCurrentStep()
}) {
HStack{
文本(步骤名称)。字体(字体。正文。重量(.bold))
}.框架(高度:50)
}
}
func setCurrentStep(){
recipe.currentStep=步骤
}
}
结构参数Row:视图{
@环境对象变量配方:配方
@状态变量sliderValue:Float=30
var参数:参数!
init(参数:参数){
self.parameter=参数
}
var body:一些观点{
VStack{
文本(parameter.name)
滑块(
//这样,通过以下方式交换这两行以复制索引超出范围错误:
//-添加步骤1、步骤2、步骤3,最后添加步骤4
//-按顺序轻敲步骤列表中的每个步骤,前三个步骤可以工作,但最后一个步骤不行
//值:$recipe.currentStep.parameters[parameter.id].current,
值:self.$sliderValue,
from:parameter.minimum,
通过:parameter.max,
作者:1.0
)
}
}
}
结构ContentView:View{
@环境对象变量配方:配方
var body:一些观点{
VStack{
名单{
ForEach(recipe.steps){步进
StepRow(步骤:步骤)
}
}
名单{
ForEach(recipe.currentStep.parameters){中的参数
ParameterRow(参数:参数)
}
}
}
}
}
同样,这方面的一个工作示例是project。我仍在浏览您的代码。但我想谈谈在函数addStepX()中引起我注意的一些问题:
func addStep1(){
让newStep=Step(id:UUID(),名称:“Step#1”,参数:[Parameter]())
currentStep=newStep
steps.insert(newStep,at:steps.count)
}
您知道steps.insert()不会触发didSet,因此不会执行didChange.send()?我建议您颠倒顺序,首先插入步骤,然后更新currentStep。这样,在完成所有更改后,只需调用一次didChange.send()
func addStep1(){
让newStep=Step(id:UUID(),名称:“Step#1”,参数:[Parameter]())
steps.insert(newStep,at:steps.count)
currentStep=newStep
}
请注意,改变这一点仍然不能解决问题,但我想我应该提到,因为这肯定是一个问题
更新
更改后,代码看起来更干净。我似乎找到了一种防止出界的方法
问题似乎是由于时间问题。数组已更新,但列表传递的参数仍然旧。最终,它将迎头赶上,但由于失控的崩溃。。。从来没有。那么为什么不让它有条件呢
请注意,我还向Text()视图添加了滑块值,以表明绑定成功:
struct参数row:View{
@环境对象变量配方:配方
@状态变量sliderValue:Float=30
var参数:参数!
init(参数:参数){
self.parameter=参数
}
var body:一些观点{
VStack{
文本(“\(parameter.name)=\(parameter.id
我仍在考虑你的问题。但只是一个简短的评论。可以使用默认值,只需在定义中使用反向引号即可。我在SwiftUI声明文件中看到苹果一直在这样做:var`default`:String
。然后,您可以使用不带引号的变量。谢谢您的提示!我一定会实现它-t