suisied
suisied

Reputation: 426

Pickerview delegate UIView: array index out of range

I created a picker view with 2 components with different data source:

func pickerView(pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusingView view: UIView?) -> UIView {
    let pickerLabel = UILabel()
    let titleLabel = firstFieldArray[row]
    let titlelabel2 = secondFieldArray[row]

    if component == 0 {
    let myTitle = NSAttributedString(string: titleLabel, attributes: [NSFontAttributeName:UIFont(name: "Georgia", size: 15.0)!,NSForegroundColorAttributeName:UIColor.blackColor()])
    pickerLabel.attributedText = myTitle
    } else {
        let myTitle = NSAttributedString(string: titlelabel2, attributes: [NSFontAttributeName:UIFont(name: "Georgia", size: 15.0)!,NSForegroundColorAttributeName:UIColor.blackColor()])
        pickerLabel.attributedText = myTitle
    }
    return pickerLabel
}

this is my data model :

class professionArrays {
    func professsionValues() -> [String] {

        return ["Engineer", "Information Technology", "Teacher"]
    }

    func subProfessionValues(profession: String) -> [String] {
        if profession == "Engineer" {
            return ["Electrical Engineer","Sex Engineer","Civil Engineer","Software Engineer"]
        } else if profession == "Information Technology" {
            return ["IT Programmer","UI/UX Designer","Tester/Debugger"]
        } else {
            return ["English Teacher", "Math Teacher", "Physics Teacher"]
        }

    }
}

Picker view is showing arrays when I select Engineer, Information Technology, and Teacher, it show their subProfessional values in the second component.

The error is triggered, for example when I select Information Technology and will select Engineer in the first component, and when I slide the second components value the error occurs:

error : fatal error: Array index out of range

edit: code inside didSelectRow delegate

func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    if component == 0 {
        let professionAndSubProfession = professionArrays()
        let currentValue = firstFieldArray[row]
        secondFieldArray = professionAndSubProfession.subProfessionValues(currentValue)
        pickerView.reloadAllComponents()
        firstProfessionCurrentValue = firstFieldArray[row]
        print(firstProfessionCurrentValue)
        selectedProfessionLabel.text = firstProfessionCurrentValue
        professionTextfield.text = firstProfessionCurrentValue


    } else {

        secondSubProfessionCurrentValue = secondFieldArray[row]
        selectedSubProfessionLabel.text = secondSubProfessionCurrentValue
        professionTextfield.text = "\(firstProfessionCurrentValue!) - \(secondSubProfessionCurrentValue!)"
        print(secondSubProfessionCurrentValue)
        pickerView.reloadAllComponents()

    }

}

Upvotes: 1

Views: 1398

Answers (1)

Justin Hammenga
Justin Hammenga

Reputation: 1091

The subprofessions for Engineer have 4 values in it's array and from the code you've shared with us, I can see that's the only issue that might create that error.

If the UIPickerView tries to show the 4th row in the second component, it will call the delegate (as you posted). In that implemention you call firstFieldArray[row]. At that point row is 3, but firstFieldArray only has 3 values, thus it's max index is 2 at that point.

In short, don't create the titleLabel if the component is 1 and otherwise, don't create titleLabel2 if the component is 0. In cases where you're not sure an array holds the amount of values you try to get the index of, do some defensive coding:

// Check if the array holds the same or more values then 'row'
if array.count > row {
    return array[row]
}

EDIT

Here's an example on how you could improve your delegate-implementation. In this way your code won't try to get an item from the wrong array. You already reload the pickerView components each time you select a new item from the picker, so it should call the DataSource & Delegates again.

// UIPickerViewDataSource
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int
    if component == 0 {
        return firstFieldArray.count
    } else {
        return secondFieldArray.count
    }
}

//UIPickerViewDelegate
func pickerView(pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusingView view: UIView?) -> UIView {
    let pickerLabel = UILabel()

    if component == 0 {
        // By placing it within the if-statement, this array won't be searched by index unnecassary.
        // So even if the second component would hold 4 or more items, it won't try to fetch 4th or higher index from this first array.
        let titleLabel = firstFieldArray[row]
        let myTitle = NSAttributedString(string: titleLabel, attributes: [NSFontAttributeName:UIFont(name: "Georgia", size: 15.0)!,NSForegroundColorAttributeName:UIColor.blackColor()])
        pickerLabel.attributedText = myTitle
    } else {
        let titlelabel2 = secondFieldArray[row]
        let myTitle = NSAttributedString(string: titlelabel2, attributes: [NSFontAttributeName:UIFont(name: "Georgia", size: 15.0)!,NSForegroundColorAttributeName:UIColor.blackColor()])
        pickerLabel.attributedText = myTitle
    }
    return pickerLabel
}

Upvotes: 2

Related Questions