1

When I try to put 2 pikers with a different number of rows on-screen with different observers. if I select in one number of the row that not exists in the second, when I move to the second picker app crash with this message: "Fatal error: Index out of range"

public enum kTrackType {
    case audio
    case text
}

class kTrack: NSObject, Identifiable {
    public var id = UUID()
    public var trakcId: String
    public var title: String
    public var type: kTrackType

    public init(id: String, title: String, type: kTrackType) {

        self.trakcId = id
        self.title = title
        self.type = type
    }
}

and this is the main struct:

struct SelectedAudioAndSubtileView: View {

let geometry: GeometryProxy

@State var subtitlesList = [kTrack(id: "t0", title: "None", type: kTrackType.text),
                            kTrack(id: "t1", title: "En", type: kTrackType.text),
                            kTrack(id: "t2", title: "Rus", type: kTrackType.text),
                            kTrack(id: "t3", title: "Spn", type: kTrackType.text)]

@State var multiAudioList =  [kTrack(id: "s0", title: "En", type: kTrackType.audio),
                              kTrack(id: "s1", title: "Rus", type: kTrackType.audio)]

@Binding var showSubtitlesPicker: Bool

@State private var selectedAudioPicker: Int = 0

@State private var selectedSubtitlePicker: Int = 0

@State private var selectedType = 0

var body: some View {
    VStack {
        Picker(selection: $selectedType, label: EmptyView()) {
            Text("Audio").tag(0)
            Text("Subtitle").tag(1)
        }
        .pickerStyle(SegmentedPickerStyle())

        Text(self.selectedType == 0 ? "Select Audio" : "Select Subtitle")
        Divider()

        if selectedType == 0 {
            Picker(selection: self.$selectedAudioPicker, label: Text("")) {

                ForEach(self.multiAudioList, id: \.id){ name in
                    Text(name.title)
                }
            }
        } else {
           Picker(selection: self.$selectedSubtitlePicker, label: Text("")) {
                ForEach(self.subtitlesList, id: \.id){ name in
                    Text(name.title)
                }
            }
        }
        Divider()
    }
    .background(Color(#colorLiteral(red: 0.9686274529, green: 0.78039217, blue: 0.3450980484, alpha: 1)))
    .offset(y: geometry.size.height - 330)
}

After recheck, the crash happened also if you have same rows in 2 pickers!

israel_b_2012
  • 277
  • 1
  • 2
  • 8

1 Answers1

2

Here is the situation :

a) the selectedValue should match the tag value, therefore in ForEach, it's better to use index not the id so that you can add tag for each items.

b) the ForEach structure is a complex one and usually to be reused for performance. So in order to force it refresh, id() modifier can be added to extra ForEach structures. There must be one ForEach without id which provides the real underlying data layer.

if selectedType == 0 {
    Picker (selection: self.$selectedAudioPicker, label: Text("")) {
        ForEach(0..<self.multiAudioList.count){ index in
            Text(self.multiAudioList[index].title).tag(index)
        }
    }

} else if  selectedType == 1 {
    Picker(selection: self.$selectedSubtitlePicker, label: Text("")) {
        ForEach(0..<self.subtitlesList.count){ index in
            Text(self.subtitlesList[index].title).tag(index)
        }.id(0)
    }
}
np2314
  • 645
  • 5
  • 14
E.Coms
  • 11,065
  • 2
  • 23
  • 35