Search code examples
iosswiftuicollectionviewuicollectionviewcell

Collection View - How to select only one cell in each section


My collection view have multiple section. What I'm trying to achieve is that user can only select one cell (answer) in each section. When the cell (answer) have been selected, the background color will change.

What i failed to do is that Example : when user click on a cell in section 1, I want to deselect only the other cell in section 1.

Below are some of my code

    @IBOutlet var step3CollectionView: UICollectionView!

    var HexColor = HexColorClass()
    var dataPageThree : json_PageThree!    
    var step3AnswerArray : [Int] = []


    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

        var frameCount = dataPageThree.step_instruction.first!.subquestions.count

        for i in 0..<frameCount{

            if indexPath.section == i {
                step3AnswerArray[i] = (dataPageThree.step_instruction.first?.subquestions[i].subquestion_selection_answerNums![indexPath.row])!


            let callCell = self.step3CollectionView.cellForItem(at: indexPath) as? Step3CollectionViewCell

            callCell!.answerLabel.backgroundColor = HexColor.hexStringToUIColor(hex: "117577")

            callCell!.answerLabel.textColor = UIColor.white

            }

        }

    }

 func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {


        let indexPaths = collectionView.indexPathsForSelectedItems
        if (indexPaths?.count) ?? 0 > 0 {

            /// If you need simple way
            for index in indexPaths! {
                if index.section == indexPath.section {
                    self.step3CollectionView.deselectItem(at: index, animated: true) // if want deselect previous selection

                    let callCell = self.step3CollectionView.cellForItem(at: index) as? Step3CollectionViewCell

                            callCell!.answerLabel.backgroundColor = UIColor.white

                            callCell!.answerLabel.textColor = UIColor.black
                    //return false  //if you do not want further selection
                }
            }


        }

        return true
    }

Need some guidance.


Solution

  • First of all, set the collectionView's allowsMultipleSelection property to true, i.e.

    override func viewDidLoad() {
        super.viewDidLoad()
        self.step3CollectionView.allowsMultipleSelection = true
    }
    

    Now, the UICollectionViewDelegate method collectionView(_: shouldSelectItemAt:) should look like,

    func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
        collectionView.indexPathsForSelectedItems?.filter({ $0.section == indexPath.section }).forEach({ collectionView.deselectItem(at: $0, animated: false) })
        return true
    }
    

    Also, don't change the backgroundColour of the cell in shouldSelectItemAt or didSelectItemAt based on cell's selection. That makes the code bulky and redundant.

    It should be done within the UICollectionViewCell subclass by overriding isSelected property.

    class Step3CollectionViewCell: UICollectionViewCell {        
        override var isSelected: Bool {
            didSet {
                self.answerLabel.backgroundColor = isSelected ? HexColor.hexStringToUIColor(hex: "117577") : .white
                self.answerLabel.textColor = isSelected ? .white : .black
            }
        }
    }
    

    With the above code, there is no need to write the color change code in collectionView(_:didSelectItemAt:) method as well. The UI for selection and de-selection of the cell will be automatically handled.

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        step3AnswerArray[indexPath.section] = (dataPageThree.step_instruction.first?.subquestions[i].subquestion_selection_answerNums![indexPath.row])!
    }