我试图让一个选择器根据先前选择器的选择动态更新。为了实现这一点,我使用了一个多维数组。不幸的是,这似乎混淆了我的 ForEach 循环,我注意到日志中有以下消息:
ForEach<Range<Int>, Int, Text> count (3) != its initial count (5).
ForEach(:content:) should only be used for *constant* data. Instead conform data to
可识别or use
ForEach(:id:content:) and provide an explicit
编号 !
这是有道理的,我猜发生的事情是我向它传递一个数组并且它一直引用它,所以就它而言,每当我向它传递另一个数组时它都会不断变化。我相信解决这个问题的方法是使用可以传递给 ForEach 的 id 参数,尽管我不确定这是否真的能解决它,我也不确定我会使用什么。另一个解决方案是以某种方式销毁 Picker 并重新创建它?有任何想法吗?
我的代码如下。如果您运行它,您会注意到在第一个选择器周围移动会导致越界异常。
import SwiftUI
struct ContentView: View {
@State private var baseNumber = ""
@State private var dimensionSelection = 1
@State private var baseUnitSelection = 0
@State private var convertedUnitSelection = 0
let temperatureUnits = ["Celsius", "Fahrenheit", "Kelvin"]
let lengthUnits = ["meters", "kilometers", "feet", "yards", "miles"]
let timeUnits = ["seconds", "minutes", "hours", "days"]
let volumeUnits = ["milliliters", "liters", "cups", "pints", "gallons"]
let dimensionChoices = ["Temperature", "Length", "Time", "Volume"]
let dimensions: [[String]]
init () {
dimensions = [temperatureUnits, lengthUnits, timeUnits, volumeUnits]
}
var convertedValue: Double {
var result: Double = 0
let base = Double(baseNumber) ?? 0
if temperatureUnits[baseUnitSelection] == "Celsius" {
if convertedUnitSelection == 0 {
result = base
} else if convertedUnitSelection == 1 {
result = base * 9/5 + 32
} else if convertedUnitSelection == 2 {
result = base + 273.15
}
}
return result
}
var body: some View {
NavigationView {
Form {
Section {
TextField("Enter a number", text: $baseNumber)
.keyboardType(.decimalPad)
}
Section(header: Text("Select the type of conversion")) {
Picker("Dimension", selection: $dimensionSelection) {
ForEach(0 ..< dimensionChoices.count) {
Text(self.dimensionChoices[$0])
}
}.pickerStyle(SegmentedPickerStyle())
}
Group {
Section(header: Text("Select the base unit")) {
Picker("Base Unit", selection: $baseUnitSelection) {
ForEach(0 ..< self.dimensions[self.dimensionSelection].count) {
Text(self.dimensions[self.dimensionSelection][$0])
}
}.pickerStyle(SegmentedPickerStyle())
}
Section(header: Text("Select the unit to convert to")) {
Picker("Converted Unit", selection: $convertedUnitSelection) {
ForEach(0 ..< self.dimensions[self.dimensionSelection].count) {
Text(self.dimensions[self.dimensionSelection][$0])
}
}.pickerStyle(SegmentedPickerStyle())
}
}
Section(header: Text("The converted value is")) {
Text("\(convertedValue) \(dimensions[dimensionSelection][convertedUnitSelection])")
}
}.navigationBarTitle("Unit Converter")
}
}
}
最佳答案
我不想回答我自己的问题,但在花了一些时间之后,我认为有必要总结一下我的发现,以防对某人有所帮助。总而言之,我试图根据第一个选择器的选择来设置第二个选择器。
如果你按原样运行我粘贴的代码,你会得到一个越界。只有当我设置 @State private var dimensionSelection = 1
并且第二个数组大于第一个数组时才会出现这种情况。如果你从较小的数组开始,你会很好,你可以通过设置 @State private var dimensionSelection = 0
来观察。有几种方法可以解决这个问题。
- 总是从最小的数组开始(不太好)
- 不使用
String
数组,而是使用实现Identifiable 的对象数组。这是上面fuzz提出的解决方案。这已经超过了超出范围的数组异常。但就我而言,我需要在 ForEach 参数中指定 id 参数。 - 扩展 String 以实现
Identifiable
只要您的字符串完全不同(这在我的简单示例中有效)。这是 gujci 提出的解决方案,他提出的解决方案看起来比我的要优雅得多,所以我鼓励你看一看。请注意,这适用于我自己的示例。我怀疑这可能是由于我们构建阵列的方式不同所致。
但是,一旦您解决了这些问题,它仍然无法正常工作,您会遇到一个问题,该问题似乎是选择器不断添加新元素的某种错误。我的印象是,要解决这个问题,每次都必须销毁 Picker,但由于我仍在学习 Swift 和 SwiftUI,所以我还没有解决这个问题。
关于swift - 使用不同的数组动态更改选择器中的选项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58369407/