-1

I have collectionView (3*3) with Images I am loading from server and I placed a checkBox in the top left corner of each cell so that I can select the cells and based on the selected cells I will get ids for the respective cells images(ids coming from server) and I am able do everything right. But, the problem is if there is are 20 images and if I check the 5 random cells which are loaded for the first time and when I scroll down to select other cells 5 other random checkBoxes are already checked and if I scroll up again some other 5 random cells are checked. It appears that the checked checkBoxes are changing positions because of the dequeue reusable property in the cellForItemAtIndexPath of UICollectionView DataSource method..

I have no Idea how to overcome this problem. Please help me If any one knows how to do this. I am posting below the code I wrote so far and some simulator screenshots for better understanding of the problem...

EditCertificatesViewController:

import UIKit
import Alamofire

protocol CheckBoxState {
    func saveCheckBoxState(cell: EditCertificateCell)
}

class EditCertificatesViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {

    @IBOutlet weak var certificatesCollectionView: UICollectionView!

    var certificatesArray = [Certificates]()

     override func viewDidLoad() {
         super.viewDidLoad()
         self.title = "Delete Certificates"
         navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
     }

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

         return certificatesArray.count
     }

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

         let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "editCertificate", for: indexPath)  as! EditCertificateCell
         if let certificateURL = URL(string: certificatesArray[indexPath.item].imagePath) {
             cell.certificateImage.af_setImage(withURL: certificateURL)
         }
         cell.certificateId.text = "\(certificatesArray[indexPath.item].imageId)"
         cell.selectCertificate.customBox()
         if selectedCellIndex.contains(indexPath.item) {
             cell.selectCertificate.on = true
         } 
         else {
             cell.selectCertificate.on = false
         }
         cell.selectCertificate.tag = indexPath.item
         cell.checkState = self
         return cell
       }
}

extension EditCertificatesViewController: CheckBoxState {

    func saveCheckBoxState(cell: EditCertificateCell) {

        if cell.selectCertificate.on == true {
            cell.selectCertificate.on = false
        } 
        else {
            cell.selectCertificate.on = true
        }

        if selectedCellIndex.contains(cell.selectCertificate.tag) {
            selectedCellIndex = selectedCellIndex.filter{$0 != cell.selectCertificate.tag}
        } 
        else {
            selectedCellIndex.append(cell.selectCertificate.tag)
        }
        print("Status1 \(selectedCellIndex.sorted { $0 < $1 })")
        //        certificatesCollectionView.reloadData()
    }
}

EditCertificateCell:

import UIKit

class EditCertificateCell: UICollectionViewCell {

    @IBOutlet weak var certificateImage: UIImageView!
    @IBOutlet weak var selectCertificate: BEMCheckBox!
    @IBOutlet weak var certificateId: UILabel!
    @IBOutlet weak var selectCertificateBtn: UIButton!

    var checkState: CheckBoxState?

    override func awakeFromNib() {
        super.awakeFromNib()
        self.selectCertificateBtn.addTarget(self, action: #selector(btnTapped(_:event:)), for: .touchUpInside)
    }

    @objc func btnTapped(_ sender: UIButton,event: UIEvent) {
        self.checkState?.saveCheckBoxState(cell: self)
    } 
}
Kuldeep
  • 4,466
  • 8
  • 32
  • 59
  • How are you selecting the tableview cells and changing the checkbox colour ? – Amit Aug 29 '18 at 10:02
  • 1
    You need to store checked item in your Model or save checked item in array and you need to check in `cellForItemAt` whether current indexPath exist in array or model. if exist then set checkmark image or not then set uncheck image. – Kuldeep Aug 29 '18 at 10:03
  • @Amit I am using collectionview cells and I not selecting cells I am using BEMCheckBox library and color changes as I have set the checked and unchecked colors in storyboard – Venkatesh Chejarla Aug 29 '18 at 10:12
  • 2
    Possible duplicate of [How can I disable UITableView's cells reloading when scrolling it?](https://stackoverflow.com/questions/14539977/how-can-i-disable-uitableviews-cells-reloading-when-scrolling-it) – finngu Aug 29 '18 at 10:14
  • 1
    Another possible duplicate of https://stackoverflow.com/questions/8304636/when-scrolling-a-table-view-cell-out-of-view-cell-text-changes?rq=1 – finngu Aug 29 '18 at 10:14
  • you must have added the selection and deselection method to it? Please Update your question with that. – Amit Aug 29 '18 at 10:16
  • @Kuldeep Can you please post your code again I would like to try it..Thanks. – Venkatesh Chejarla Aug 29 '18 at 15:02
  • @finngu my concern is totally different from the above two you have mentioned. But thanks for the suggestions mate... – Venkatesh Chejarla Aug 30 '18 at 04:50
  • @VenkateshChejarla I am sure it is different, but ultimately it boils down to cell recycling and I am sure these other two questions can help you solve your problem. Also they might help others, who want to learn about the concepts of UITableView and UICollectionView. – finngu Aug 30 '18 at 09:33
  • @finngu But, this answer [https://stackoverflow.com/a/52074887/9326980] and this [https://stackoverflow.com/a/52075673/9326980] helped me solve my problem.. Thanks you – Venkatesh Chejarla Aug 30 '18 at 09:53

3 Answers3

2

CollectionView dequeue's your cell. To rid of this you need to maintain array of selected certificates. Follow below procedure.

  1. Create an array arrSelectedIndex : [Int] = []

  2. In cellForRow,

    • First check either current index in available in arrSelectedIndex or not? If yes, then make your cell as selected otherwise keep it uncheck.

    • Give tag to your check button as like this buttonCheck.tag = indexPath.item

  3. If you wanted to select images on check button action, do below.

    • Get the button tag let aTag = sender.tag
    • Now check wther this index is available in arrSelectedIndex or not? If yes then remove that index from from the arrSelectedIndex otherwise append that array.
    • reload your cell now.
  4. If you wanted to select images on didSelectItem instaead check button action, do below.

    • Now check wther this selected index (indexPath.item) is available in arrSelectedIndex or not? If yes then remove that index from from the arrSelectedIndex otherwise append that array.
    • reload your cell now.

As this procedure is lengthy so I can only explain you how to do this. If need further help then you can ask.

dahiya_boy
  • 9,298
  • 1
  • 30
  • 51
  • this is working fine but every time when checking a checkBox I am reloading collectionView this makes all the images flash for a sec.. – Venkatesh Chejarla Aug 29 '18 at 13:34
  • what if I don't call collectionView.reloadData() in the checkBox method – Venkatesh Chejarla Aug 29 '18 at 13:35
  • @VenkateshChejarla In that case your collection button appeared be in old state i.e. initially your image is unselected, now when you select any image every thing work in the code but on UI it still shows same image as unselected. To update data you need to reload it. – dahiya_boy Aug 29 '18 at 13:56
  • @VenkateshChejarla your UI is flashed bcz you reloading your entire collectionView. So better if you use [reloadItemsAtIndexPaths](https://developer.apple.com/documentation/uikit/uicollectionview/1618055-reloaditemsatindexpaths). It is fast and avoid your screen to flash. It is the only procedure that you have to do. – dahiya_boy Aug 29 '18 at 13:58
  • I checked everything and it is working very fine for me even without calling collectionView.reloadData() or may be in my case I don't need to call it I think.. But, I don't what do you mean by "UI shows the same image as unselected".. I am not selected the image but the checkbox which is in the cell and it updates when I tap and thats all I want. btw you can check my code(edited) in EditCerttificatesViewController and EditCertificateCell how I did it.. – Venkatesh Chejarla Aug 29 '18 at 14:52
  • Anyway, thanks a lot for helping me with this problem mate I might not have done this without your help.. – Venkatesh Chejarla Aug 29 '18 at 14:54
1

This is expected. Because you are reusing the cells.

Consider this. You select the first 2 cells, and now scroll down. This function of yours will be called func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {. Now this might get the views from the first 2 cells, that you had selected, and their checkboxes are already selected too.

You need to unset them, and set them, depending upon their last state.

I would recommend adding another property isCertificateSelected to your Certificate model. Each time the user taps on a cell, you retrieve the model, and set/unset this bool. When collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) is called, you retrieve the isCertificateSelected again, and set the checkbox accordingly.

Shamas S
  • 7,507
  • 10
  • 46
  • 58
1

Create an array var Status1CheckList = [Int]()

And in cellForItemAt indexPath check the condition like

if Status1CheckList.contains(indexPath.row) {
    cellOfCollection.CheckBtn.setImage(UIImage(named: "check"), for: .normal)
} else {
    cellOfCollection.CheckBtn.setImage(UIImage(named: "uncheck"), for: .normal)
}
cellOfCollection.CheckBtn.tag = indexPath.row
cellOfCollection.CheckBtn.addTarget(self, action: #selector(self.checkList), for: .touchUpInside)

And checklist method, After selecting button reload the collectionview

 @objc func checkList(_ sender: UIButton) {
        if Status1CheckList.contains(sender.tag) {
            Status1CheckList =  Status1CheckList.filter{ $0 != sender.tag}
        } else {
            Status1CheckList.append(sender.tag)
        }
        print("Status1 \(Status1CheckList.sorted { $0 < $1 })")
    self.collectionviewObj.reloadData()
}
chandra1234
  • 357
  • 6
  • 15