Reputation: 15266
I am trying to create a PickerView
which has sections
each item conforms to "id" and I am tagging the Text
with the item's id (which is unique and I verified that there are no clashes)
The PickerView
seems to disregard the tags I am assigning and chooses all rows with the same corresponding index for each section
I also tried tagging each item with a random UUID to check the behavior and it seems to continue
struct ExperimentPickerView: View {
@StateObject var localExperiment = RunningExperiment.active
@StateObject var experiments = RemoteExperiments.instance
@State var picked : Int = -1
var body: some View {
Picker("active", selection: $picked) {
ForEach(Array(experiments.experiments.enumerated()), id: \.offset) { i,experiment in
Section(header: Text("\(experiment.name)")) {
ForEach(Array(experiment.variations.enumerated()), id: \.offset) { j,variation in
// Text("\(variation.name) \(variation.id)").tag(variation.id)
Text("\(variation.name) \(variation.id)").tag(UUID().description)
}
}
}
}.id(picked).onReceive([self.picked].publisher.first()) { (value) in
print(value) // prints the row number and not the id of the element
}
}
}
struct Experiment : Codable, Equatable, Identifiable {
var id: Int {
var hasher = Hasher()
name.hash(into: &hasher)
variations.hash(into: &hasher)
return hasher.finalize()
}
let name : String
let variations: [Variation]
enum CodingKeys: String, CodingKey {
case name
case variations
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let n = try container.decode(String.self, forKey: .name)
name = n
let a = try container.decode(AnyCodable.self, forKey: .variations)
print(a)
let b = a.value as! [[String:Any]]
var vars = [Variation]()
for v in b {
if let na = v["name"], let nna = na as? String {
vars.append(Variation(name: nna, experiment: n))
}
}
variations = vars
}
}
struct Variation: Codable, Equatable, Identifiable, Hashable {
var id: Int {
var hasher = Hasher()
name.hash(into: &hasher)
experiment.hash(into: &hasher)
let hashValue = hasher.finalize()
return hashValue
}
let name: String
var experiment: String
enum CodingKeys: String, CodingKey {
case name, experiment
}
}
Upvotes: 0
Views: 67
Reputation: 257789
The tag
should be same type as selection
, because it is used to match picked row. And id
should not be modified in this scenario, because it reinitialises picker completely.
Provided code is not testable so here is just a demo of solution (simplified replication of your scenario).
Tested with Xcode 12.1 / iOS 14.1.
struct Pair: Hashable {
var section = ""
var row = -1
}
struct ExperimentPickerView: View {
var sections = ["A", "B", "C"]
var rows = Array(0..<5)
@State private var picked = Pair(section: "B", row: 2) // << here !! (initialised demo)
var body: some View {
Picker("active", selection: $picked) {
ForEach(sections, id: \.self) { section in
Section(header: Text("\(section)").bold()) {
ForEach(rows, id: \.self) { i in
Text("\(section) \(i)")
.tag(Pair(section: section, row: i)) // << match !!
}
}
}
}.onReceive([self.picked].publisher.first()) { (value) in
print(value)
}
}
}
Upvotes: 0