swift - 使用不同的数组动态更改选择器中的选项

标签 swift swiftui

我试图让一个选择器根据先前选择器的选择动态更新。为了实现这一点,我使用了一个多维数组。不幸的是,这似乎混淆了我的 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 来观察。有几种方法可以解决这个问题。

  1. 总是从最小的数组开始(不太好)
  2. 不使用String 数组,而是使用实现Identifiable 的对象数组。这是上面fuzz提出的解决方案。这已经超过了超出范围的数组异常。但就我而言,我需要在 ForEach 参数中指定 id 参数。
  3. 扩展 String 以实现 Identifiable 只要您的字符串完全不同(这在我的简单示例中有效)。这是 gujci 提出的解决方案,他提出的解决方案看起来比我的要优雅得多,所以我鼓励你看一看。请注意,这适用于我自己的示例。我怀疑这可能是由于我们构建阵列的方式不同所致。

但是,一旦您解决了这些问题,它仍然无法正常工作,您会遇到一个问题,该问题似乎是选择器不断添加新元素的某种错误。我的印象是,要解决这个问题,每次都必须销毁 Picker,但由于我仍在学习 Swift 和 SwiftUI,所以我还没有解决这个问题。

关于swift - 使用不同的数组动态更改选择器中的选项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58369407/

相关文章:

ios - 打开 TableView 并选择行

ios - undefined symbol :___ isplatformversionatleast

SwiftUI DatePicker 星期几语言

ios - SwiftUI 在 NavigationLink View 中隐藏 TabView 栏

ios - 在添加和删除第二个按钮时,在SwiftUI中保持导航项按钮对齐

objective-c - Swift 协议(protocol)可以定义为动态的吗?

swift - 将 Alecrim Core Data 与 Swift 3 结合使用

ios - 如何将字符串日期转换为 NSDate?

macos - 删除/更改列表突出显示颜色(或调整项目之间的空间)

SwiftUI 崩溃 : "precondition failure: attribute failed to set an initial value: 71”