Swift reusable cells with corrupted indexPath

Looking for some advice. Developing a gym app where the user will enter reps and lbs lifted per set per exercise. The collection of SET (per exercise) is a collection view which scrolls left to right. Here is my problem – after 4 sets are added and you scroll left and right the indexPath seems to get corrupted such that set 4 might have the values from set 1. I think it has to do with reloading the visible values and reusable cells. After the 5th cell I get a crash because of a nil value in the indexPath. The data is dynamically entered by the user and can be accessed in the Constants.Other.setsFromCollectionView variable (until thst also get corrupted with te scrolling).

Really stumped on this one. Any help would be very much appreciated.

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell

    self.collectionView.isPrefetchingEnabled = false
    print("Cell tag:  ", cell.tag)
     
    let i = indexPath.item / numberOfSets
    let j = indexPath.item % numberOfSets
    let item = j * 5+i
     
    if Constants.view_specific_workout_from_homeScrren.posted_bool == true {
      cell.repsText.text = "0"
      cell.weightText.text = "0"
      Constants.view_specific_workout_from_homeScrren.posted_bool = false
    }
    cell.initialize(withDelegate: self)
     
    cell.layer.cornerRadius = 15.0
    cell.layer.borderWidth = 0.0
    cell.layer.shadowColor = UIColor.black.cgColor
    cell.layer.shadowOffset = CGSize(width: 0, height: 0)
    cell.layer.shadowRadius = 3.0
    cell.layer.shadowOpacity = 0.8
    cell.layer.masksToBounds = false //<-
    cell.layer.borderColor = UIColor.systemBlue.cgColor
     
    cell.setnumber.text = ("Set " + String(indexPath.row + 1))

    return cell
  }

Accepted Reply

Claude you are amazing - thank you very much. This has solved my issue (and you taught me more thsn knew before)!

Replies

I suppose this code is inside

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

I miss something in your code:

  • you define item but is seems you never use it (at least in the posted code). What is the purpose of:
    let i = indexPath.item / numberOfSets
    let j = indexPath.item % numberOfSets
    let item = j * 5+i
  • Could you show the code where you get the crash ?
  • Could you show the complete code of the class (problem may be elsewhere).
extension WorkotuCreationCollectionTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
   
  func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return numberOfSets
  }
   
  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
  
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell

    print("Array:", Constants.Other.setsFromCollectionView)

    self.collectionView.isPrefetchingEnabled = false
     
    if Constants.view_specific_workout_from_homeScrren.posted_bool == true {
      cell.repsText.text = "0"
      cell.weightText.text = "0"
      Constants.view_specific_workout_from_homeScrren.posted_bool = false
    }
    cell.initialize(withDelegate: self)
     
    cell.layer.cornerRadius = 15.0
    cell.layer.borderWidth = 0.0
    cell.layer.shadowColor = UIColor.black.cgColor
    cell.layer.shadowOffset = CGSize(width: 0, height: 0)
    cell.layer.shadowRadius = 3.0
    cell.layer.shadowOpacity = 0.8
    cell.layer.masksToBounds = false //<-
    cell.layer.borderColor = UIColor.systemBlue.cgColor
     

    cell.setnumber.text = ("Set " + String(indexPath.row + 1))

    return cell
  }
   
  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: 100, height: 110)
  }
   
  func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    Constants.WorkoutSelectionVars.indexPath = indexPath
     Constants.WorkoutSelectionVars.indexPathRow = indexPath.row
  }
   
  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 3
  }

  // Search Bar Stuff Below!
  func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    filteredData = searchText.isEmpty ? WorkoutNameArray : WorkoutNameArray.filter({ (dat) -> Bool in
      dat.range(of: searchText, options: .caseInsensitive) != nil
    })
//    dropButton.dataSource = filteredData
//    dropButton.show()
  }
   
//  func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
//    searchBar.setShowsCancelButton(true, animated: true)
//    for ob: UIView in ((searchBar.subviews[0] )).subviews {
//      if let z = ob as? UIButton {
//        let btn: UIButton = z
//        btn.setTitleColor(UIColor.white, for: .normal)
//      }
//    }
//  }

  func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
    searchBar.showsCancelButton = false
    // ------------------------------------------- //
    // Trying some new stuff here - might not work //
    // ------------------------------------------- //
    let exerciseNumber_string = String(collectionView.tag)
    let exercise_chosen = searchBar.text ?? " "
    let full_concat = exercise_chosen + exerciseNumber_string
     
    let last1 = String(full_concat.suffix(1))
     
    for values in Constants.View_Specific_Workout.final_exercise_array {
      let temp = String(values.suffix(1))
      if last1 == temp {
        Constants.View_Specific_Workout.final_exercise_array.removeAll { $0 == values }
      }
      else {
        print("first element for the new exercisde")
      }
    }
     
    Constants.View_Specific_Workout.final_exercise_array.append(full_concat)
    print("exercise aname array is: ",Constants.View_Specific_Workout.final_exercise_array)
    print("the last number of the newly added joint is:", last1)
    // ------------------------------------------- //
    // ------------------------------------------- //
     
  }

  func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
    searchBar.resignFirstResponder()
    searchBar.text = ""
    filteredData = WorkoutNameArray
    //dropButton.hide()
  }
}

Thanks.

Where exactly does it crash ? What is the exact error message ?

I do not find the initialiser for cell.initialize(withDelegate:)

  • So the issue is when the user enters data into on of the cells (reps or lbs) and has not than 5 sets - when you scroll left and right to see the data the index get all messed up and the original data chnges. Does that make sense?

  • I understand the use case. But It would be helpful to know on which line of code the crash occurs.    What about cell.initialize(withDelegate:) ?

  • So its interesting - if I comment out the cell.initialize it does not crash. What happens is the values in the cells after cell 5 get all mixed up. For example if I add cell #6, the value is what was stored in cell 1 and cell 1 value has cell #6 value. It seems to have to do with the scolling and the visible cells on the screen. I can send you the project if that would help AND you have the time.

Deleted

Check the cells you dequeue are the cells defined in the CollectionView.

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell

Check in particular as! CollectionViewCell

Cells are not data store. You need to have a dataSource.

So, in

  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

you should populate the cell with data coming from a dataSource.

Add some code in WorkotuCreationCollectionTableViewCell:

    struct Content { 
        var reps: Int
        var weight: Int
    }

    var data : [Content] = [Content(reps: 1, weight: 0)] 

Add code in   func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        cell.repsLabel.text = String(data[indexPath.row].reps)  
        cell.weightLabel.text = String(data[indexPath.row].weight) 

Claude you are amazing - thank you very much. This has solved my issue (and you taught me more thsn knew before)!