PICKERVIEWS WITH DIFFERENT DATASET

Hello everyone

I have tried some things, but still blocked with my project.

In one view, I have 3 different pickerviews, and I need them connected but with different dataset. I explain with an example.

On the first pickerview the user selects the trademark of a car (example: Ferrari, Porsche...), and then depending on the trade selected, I need to display on the second pickerview, the models of the kind of car (example if Porsche selected: 911, Carrera, Cayman...), continuing on this way, I need the third pickerview to display the years of fabrication of this model of car selected.

How can I do this selection of different datas depending on the data selected on the previous pickerview?

Did you consider a picker with 3 components ?


    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 3
    }


Then first component would be populated with car brands

The second would be populated with the entry of the dictionary which lists models

etc…


You set the content in


    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        
    }



Here is a start with just 2 components, you would have to add a third with years of fab



    private let brandsNames = [ "Porsche", "Audi", "Ferrari"]
    private let modelsDico = [ "Porsche":["911", "Carrera","Cayman"], "Audi": ["i8", "TT"], "Ferrari": ["TestaRossa", "GT"]]

// Dico for years could have entries as "Porsche-911": [1957, 1958] 
// Or, if you are sure there are not similar names in 2 different brands, just use the model name as key.

    // MARK:- Picker Data Source Methods
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 2     // You will define 3
    }
  
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        switch component {
        case 0 : return brandsNames.count
        case 1:
            let selected = picker.selectedRow(inComponent: 0)
            let selectedName = brandsNames[selected]
            return modelsDico[selectedName]?.count ?? 0
        default: return 0
        }
    }

    // MARK:- Picker Delegate Methods

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {

        switch component {
        case 0 : return brandsNames[row]
        case 1:
            let selected = picker.selectedRow(inComponent: 0)
            let selectedName = brandsNames[selected]
            return modelsDico[selectedName]?[row] ?? ""
        default: return ""
        }
    }


    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {

            picker.reloadComponent(1)     // That reloads the models when you select another brand
        }


EDITED for Triple picker (my data are really phony here !):


    private let brandsNames = ["Porsche", "Audi", "Ferrari"]

    private let modelsDico = [
        "Porsche":["911", "Carrera","Cayman"],
        "Audi": ["i8", "TT"],
        "Ferrari": ["TestaRossa", "GT"]]

// If a model can only exist once !
    private let yearsModelsDico = [
        "911":[1959, 1960, 1961], "Carrera": [1970,1971], "Cayman": [2000,2001],
        "i8":[2010, 2011], "TT": [2000,2001],
        "TestaRossa":[2002, 2003], "GT": [1990,1991, 1992, 1993]]

    // MARK:- Picker Data Source Methods

    func numberOfComponents(in pickerView: UIPickerView) -> Int {

        return 3
    }
   
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {

        switch component {
        case 0 : return brandsNames.count
        case 1:
            let selected = picker.selectedRow(inComponent: 0)
            let selectedName = brandsNames[selected] 
            return modelsDico[selectedName]?.count ?? 0
        case 2:
            let selectedBrand = picker.selectedRow(inComponent: 0)
            let selectedBrandName = brandsNames[selectedBrand]
            let selectedModel = picker.selectedRow(inComponent: 1)
            let selectedModelName = modelsDico[selectedBrandName]?[selectedModel] ?? ""
            return yearsModelsDico[selectedModelName]?.count ?? 0
        default: return 0
        }
    }

    // MARK:- Picker Delegate Methods

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {

        switch component {
        case 0 : return brandsNames[row] 
        case 1:
            let selected = picker.selectedRow(inComponent: 0)
            let selectedName = brandsNames[selected]
            return modelsDico[selectedName]?[row] ?? "" 
        case 2:
            let selectedBrand = picker.selectedRow(inComponent: 0)
            let selectedBrandName = brandsNames[selectedBrand]
            let selectedModel = picker.selectedRow(inComponent: 1)
            let selectedModelName = modelsDico[selectedBrandName]?[selectedModel] ?? ""
            return String(yearsModelsDico[selectedModelName]?[row] ?? 0)
        default: return ""
        }
    }

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
       
        if component == 0 {
            picker.reloadComponent(1)
            picker.reloadComponent(2)
       }
        if component == 1 {
            picker.reloadComponent(2)
        }
   }

Your first pickerView (A) will call the delegate method pickerView:didSelectRow:inComponent to indicate that it has been changed and has a new value (Porsche or Toyota). When that happens you assign a default value in your Model to B (911 or Prius) and "reloadAllComponents" of the other pickerViews (B and C). As B and C reload they will call the following delegate method (among others):


-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{


In that method you examine the pickerView to determine which of the three pickerViews it is (A, B or C). To do that you can assign an IBOutlet reference name or a tag. Based on which pickerView is calling the method, and the value of the other pickerView selections, you say "911" or "Prius" or "1973" or "2002".



I recommend you use an MVC model. Be clear on what the Model is 'thinking'. Then you can load the View (the pickerView) through the Controller (the delegate methods being called) with the value appropriate based on the Model. Don't rely directly on the "row" value of the pickerView to decide what another pickerView should be displaying. Use that row value to change the Model. It's harder at first but brings the reward that you will not find yourself buried in spagetti code.





PICKERVIEWS WITH DIFFERENT DATASET
 
 
Q