Изменять параметры в средстве выбора динамически, используя различные массивы - PullRequest
1 голос
/ 14 октября 2019

Я пытаюсь заставить Пикер динамически обновляться в зависимости от выбора предыдущего Пикера. Для этого я использую многомерный массив. К сожалению, это, кажется, смущает мой цикл 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 id !

Это имеет смысл, я предполагаю, что происходит то, что я передаю ему один массив, и он продолжает ссылаться на него, насколько это возможноэто постоянно меняется, когда я передаю ему другой массив. Я считаю, что способ решить эту проблему - использовать параметр id, который можно передать в ForEach, хотя я не уверен, что это действительно решит проблему, и я не уверен, что бы я использовал. Другим решением было бы как-то уничтожить сборщик и воссоздать его? Есть идеи?

Мой код следующий. Если вы запустите его, вы заметите, что перемещение вокруг первого средства выбора может привести к исключению за пределами границ.

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")
        }
    }
}

Ответы [ 2 ]

1 голос
/ 19 октября 2019

Я не хочу отвечать на свой вопрос, но, потратив некоторое время на него, я думаю, что стоит обобщить мои выводы на случай, если это кому-нибудь поможет. Подводя итог, я пытался установить второй пикер в зависимости от выбора первого пикера.

Если вы запустите код, который я вставил как есть, вы получите выход за пределы. Это только в том случае, если я установил @State private var dimensionSelection = 1, а второй массив больше, чем первый массив. Если вы начнете с меньшего массива, все будет хорошо, что вы можете наблюдать, установив @State private var dimensionSelection = 0. Есть несколько способов решить эту проблему.

  1. Всегда начинать с наименьшего массива (не очень)
  2. Вместо использования массива String, используйте массив объектов, реализующихОпознаваемая. Это решение, предложенное Fuzz выше. Это вышло за пределы исключенного массива. В моем случае, однако, мне нужно было указать параметр id в параметрах ForEach.
  3. Расширить строку для реализации Identifiable, пока все ваши строки различны (что работает в моем тривиальном примере). Это решение, предложенное Gujci, и его решение выглядит намного элегантнее, чем мое, поэтому я рекомендую вам взглянуть. Обратите внимание, что это работает в моем собственном примере. Я подозреваю, что это может быть связано с тем, как мы построили массивы по-разному.

ОДНАКО, как только вы преодолеете эти проблемы, это все равно не будет работать, вы столкнетесь с проблемой, которая кажется какой-то ошибкойгде сборщик продолжает добавлять новые элементы. У меня сложилось впечатление, что для того, чтобы обойти это, нужно каждый раз уничтожать сборщик, но, поскольку я все еще изучаю Swift и SwiftUI, у меня не получилось обойти это.

0 голосов
/ 14 октября 2019

Итак, в соответствии с документацией Apple вы должны убедиться, что элементы массива равны Identifiable, как вы упомянули.

Тогда вы захотите использовать ForEach следующим образом:

struct Dimension: Identifiable {
    let id: Int
    let name: String
}

var temperatureUnits = [
    Dimension(id: 0, name: "Celsius"),
    Dimension(id: 1, name: "Fahrenheit"),
    Dimension(id: 2, name: "Kelvin")
]

ForEach(temperatureUnits) { dimension in
    Text(dimension.name)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...