Search code examples
iosswiftuicollectionviewuisegmentedcontrol

How to change data based on segmentedControl index


I am trying to change the data of a collectionView based on the index of a segment control. When I run my code I get a crash. Can someone tell me what I did wrong here? I was following different methods I found. The error code I get with the crash is

Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1da72fde0)

The error occurs when I try switching between segment indexes. and happens on the line

cell.trendsLabel.text = maleTrends[indexPath.row]

of

 func handleSegControlTapped(for header: HomeViewHeaderReusableView)


 var header: HomeViewHeaderReusableView?



    func handleSegControlTapped(for header: HomeViewHeaderReusableView) {

        collectionView.reloadItems(at: collectionView.indexPathsForVisibleItems) 

        switch header.segmentedControl?.selectedSegmentIndex {
        case 0:
            print("Display female trends")

            header.segmentedControl?.selectedSegmentIndex = 0

            let indexPath = IndexPath()
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "homeViewCell", for: indexPath) as! CurrentTrendsCell
            cell.trendsLabel.text = femaleTrends[indexPath.row]
            cell.trendsImageView.image = femaleImages[indexPath.row]

        case 1:
            print("Display male trends")
            header.segmentedControl?.selectedSegmentIndex = 1
            let indexPath = IndexPath()

            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "homeViewCell", for: indexPath) as! CurrentTrendsCell
                     cell.trendsLabel.text = maleTrends[indexPath.row]
            cell.trendsImageView.image = maleImages[indexPath.row]

        default:
            break
        }

        collectionView.reloadData()

    }



  func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

        let segmentedOutlet = header?.segmentedControl

        switch segmentedOutlet?.selectedSegmentIndex {
        case 0: return femaleTrends.count

       case 1: return maleTrends.count

        default: print("opps, cant load data")
        }

        return 0

    }


    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "homeViewCell", for: indexPath) as! CurrentTrendsCell

        let segmentedOutlet = header?.segmentedControl

        switch segmentedOutlet?.selectedSegmentIndex {
        case 0: cell.trendsLabel.text = femaleTrends[indexPath.row]
        cell.trendsImageView.image = femaleImages[indexPath.row]

        case 1: cell.trendsLabel.text = maleTrends[indexPath.row]
        cell.trendsImageView.image = maleImages[indexPath.row]

        default: break

        }
        return cell


    }

  func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "homeViewReuseCell", for: indexPath) as! HomeViewHeaderReusableView

        headerView.delegate = self 
        return headerView
    }


Edit

Here is the code in the header


class HomeViewHeaderReusableView: UICollectionReusableView {

    @IBOutlet weak var segmentedControl: UISegmentedControl?


    var delegate: HomeSegmentedControl?



     // MARK: Handler
    @objc func handleSegTapped() {
        delegate?.handleSegControlTapped(for: self)
    }



    @IBAction func segmentedTapped(_ sender: Any) {
        handleSegTapped()

        // change data based on female / male


    }


 override func awakeFromNib() {
         super.awakeFromNib()

        segmentedControl?.tintColor = .clear
        segmentedControl?.layer.borderColor = UIColor.clear.cgColor
        segmentedControl?.setBackgroundImage(UIImage(), for: .normal, barMetrics: .default)


        segmentedControl?.setTitleTextAttributes([
            NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 11),
            NSAttributedString.Key.foregroundColor: UIColor.lightGray

            ], for: .normal)

        segmentedControl?.setTitleTextAttributes([
            NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 11) ,
            NSAttributedString.Key.foregroundColor: UIColor.white
            ], for: .selected)


        segmentedControl?.selectedSegmentIndex = 0



    }



Solution

  • Never, never ever call dequeueReusableCell outside of cellForRow/ cellForItem. The cell is dequeued but gets deallocated when the method exits. Unlike the collection/table view data source method the cell is not returned anywhere.

    Edit:

    Replace

    var header: HomeViewHeaderReusableView?
    

    with

    var selectedSegmentIndex = 0
    

    Then replace the entire method handleSegControlTapped with

    func handleSegControlTapped(for header: HomeViewHeaderReusableView) {
        selectedSegmentIndex = header.segmentedControl!.selectedSegmentIndex
        collectionView.reloadData()
    }
    

    As the data source methods handle the state of the segmented control simply reloading the collection view is sufficient.

    Then change numberOfItemsInSection to

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    
        switch selectedSegmentIndex {
        case 0: return femaleTrends.count
        case 1: return maleTrends.count
        default: print("opps, cant load data")
           return 0
        }
    }
    

    and change the other data source methods accordingly.