将 Slider 值绑定到 EnvironmentObject 中的嵌套数组时,索引超出范围
Posted
技术标签:
【中文标题】将 Slider 值绑定到 EnvironmentObject 中的嵌套数组时,索引超出范围【英文标题】:Index out of range when binding a Slider value to a nested array in EnvironmentObject 【发布时间】:2019-11-22 07:08:08 【问题描述】:说明:
我有一个具有以下层次结构的模型:
食谱 ...步骤(一个数组) ...当前步骤 ......参数(一个数组) …………最低 .........最大 .........默认 .........当前该模型运行良好。我可以添加步骤、参数,并将当前步骤设置为名为@987654325@ 的@EnvironmentObject
。
我创建了一个示例项目here,其中包含两个步骤和参数列表,以及三个用于在三个硬编码步骤中添加一个步骤的按钮,每个步骤包含 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)
同样,项目here 就是一个工作示例。
【问题讨论】:
我还在处理你的问题。但只是一个快速的评论。您可以使用默认值,您只需在定义中使用反引号。我一直在 SwiftUI 声明文件中看到 Apple 这样做:var `default`: String
。然后你可以使用不带引号的变量。
感谢您的提示!我一定会实现它 - 这是一个CoreImage
应用程序,这是更正确的使用方式。
@kontiki,我确实有一个本地示例项目。我可能需要 30 分钟来清理一些东西并配合我所有的尝试。这将是我在 Xcode 13 中第一次使用 GitHub。如果需要,请告诉我。
那太好了。我的代码比英语更流利;-)
@kontiki,上传到github.com/justdfd/ListBug我也会更新问题。
【参考方案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
)
【讨论】:
这可能是索引越界错误方面的问题。 (它仍然没有正确绑定。最终我需要将两个列表联系在一起 - 有一个步骤行设置当前步骤 - 并且想在“主”应用程序中一次处理一件事。我有一个想法如何更改设置位置currentStep
。我会看看这是否有助于消除第一个问题。感谢您的注意,我同意,这并不意味着我正确绑定了滑块。
快到了。我现在可以绑定到我的模型(索引超出范围错误)或在本地绑定并且没有索引超出范围错误。你让我在正确的道路上移动currentStep
的更新。我将更新我的问题和示例项目。
我更新了我的答案,并进行了修复。让我知道它是否有效。
它有效,现在我需要了解一些细节。我是在反应式环境中工作的新手,更不用说Compose
,但我确实有时间。查看您的代码,您是否使用Text
中的条件来检查名称(并且需要我初始化为空字符串)?至于滑块,您是否建议 UI 更新得如此之快,实际上在列表中显示了错误的参数,但条件有助于使其如此之快以至于人眼看不到它? :-) 最后,感谢您,您认为这是我可以做得更好的事情吗?这些条件似乎有点不直观。
不客气。我确实认为您的代码应该可以工作。时间问题,我认为这是一个错误,而我的解决方案是一种解决方法。但我不能确定这种行为是否真的是一个错误,或者它是否按预期工作。文本中的条件更相同。这是为了防止越界,毕竟我们访问的是同一个元素。您对视觉更新是正确的。我不知道它是不是太快以至于我们看不到它,还是它只是一些永远无法绘制的内部逻辑。无论如何,事实证明它太快了,不会造成麻烦。最后的建议...(续)。以上是关于将 Slider 值绑定到 EnvironmentObject 中的嵌套数组时,索引超出范围的主要内容,如果未能解决你的问题,请参考以下文章