2-component pickerView with array errors

Hi! I am working on a pickerView with two components. I have an array (myHorses) with one stallion and two mares. If I run the app I get an "out of range" error at line 33. If I add another stallion to the array this goes away. Also, the image outlets are showing the images for the stallion on both outlets. How can I make the app work when there is just one member of a gender, and also show the correct images?

Code Block import UIKit
class BreedingViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
    let myStallions = myHorses.filter({ $0.gender == "Stallion" })
    let myMares = myHorses.filter({ $0.gender == "Mare" })
    var sireIndex = 0
    var damIndex = 0
    @IBOutlet weak var horsePicker: UIPickerView!
@IBOutlet weak var sireBaseLayer: UIImageView!
    @IBOutlet weak var damBaseLayer: UIImageView!
  
    var recievedString: String = ""
    func numberOfComponents(in sirePicker: UIPickerView) -> Int {
        return 2
    }
  
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if component == 0 {
            return myStallions.count
        }
        else {
            return myMares.count
        }
    
    }
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
            let stallions = myStallions[row]
            let mares = myMares[row]
      
            if component == 0 {
                return "\(stallions.name), health: \(stallions.health)"
            }
            else {
                return "\(mares.name), health: \(mares.health)"
            }
        }
    func pickerView(_ horsePicker: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        var chosenSire = myHorses[sireIndex]
        var chosenDam = myHorses[damIndex]
        if component == 0 {
            let theSire = myHorses[row]
            sireNameOutlet.text = theSire.name
            chosenSire = theSire  
sireBaseLayer.image = chosenSire.basePhenotype
        }
        else {
            let theMare = myHorses[row]
            damNameOutlet.text = theMare.name
            chosenDam = theMare
damBaseLayer.image = chosenDam.basePhenotype
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        horsePicker.delegate = self
        horsePicker.dataSource = self
    }
    
}


Answered by OOPer in 644165022
As far as I see the definition of your pickerView(_ :numberOfRowsInComponent:),
row in your pickerView(_:titleForRow:forComponent:) will take arbitrary number in range:
  • 0..<myStallions.count when component == 0

  • 0..<myMares.count otherwise

So, if count of myStallions and count of myMares are not the same, row may exceed the range of valid index for the smaller Array.

To make two methods consistent, your pickerView(_:titleForRow:forComponent:) should be something like this:
Code Block
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if component == 0 {
let stallions = myStallions[row]
return "\(stallions.name), health: \(stallions.health)"
} else {
let mares = myMares[row]
return "\(mares.name), health: \(mares.health)"
}
}



You may also need to fix pickerView(_:didSelectRow:inComponent:), but it seems not complete yet. So, it would be another issue in the future.
Accepted Answer
As far as I see the definition of your pickerView(_ :numberOfRowsInComponent:),
row in your pickerView(_:titleForRow:forComponent:) will take arbitrary number in range:
  • 0..<myStallions.count when component == 0

  • 0..<myMares.count otherwise

So, if count of myStallions and count of myMares are not the same, row may exceed the range of valid index for the smaller Array.

To make two methods consistent, your pickerView(_:titleForRow:forComponent:) should be something like this:
Code Block
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if component == 0 {
let stallions = myStallions[row]
return "\(stallions.name), health: \(stallions.health)"
} else {
let mares = myMares[row]
return "\(mares.name), health: \(mares.health)"
}
}



You may also need to fix pickerView(_:didSelectRow:inComponent:), but it seems not complete yet. So, it would be another issue in the future.
That works, I also updated didSelectRow and now it's good
2-component pickerView with array errors
 
 
Q