Search code examples
swiftuicollectionviewsectionsmultipleselection

Can I have some CollectionView sections be single selection and others be multiple selection?


Some of my CollectionView sections should only allow users to select single options, and others should allow users to pick any amount in that section. How can I design a CollectionView such that some sections allow multiple selection and others only allow single selection?

I'm building a food delivery app that asks users to select from a screen of choices to customize their order (i.e. picking sugar level or choosing a side drink). I currently have the list of customizable options displayed as one UICollectionView with different sections specifying what exactly they'd like to customize. To clarify, a section header might say "Choose your Drink" and the cells in this section would list out "Coke," "Pepsi," etc.

class MenuDetailVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

    var item: MenuItem?
    var cvCellID = "collectionviewid"
    var masterCustomizables: [MasterCustomize]?

    override func viewDidLoad() {
        super.viewDidLoad()
        masterCustomizables = item?.customizables
        setupCV()
    }
//...

    func setupCV() {
        masterCollectionView.dataSource = self
        masterCollectionView.delegate = self
        masterCollectionView.register(CustomizeCellHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "headerID")
        masterCollectionView.register(CustomizeCell.self, forCellWithReuseIdentifier: cvCellID)
        masterCollectionView.backgroundColor = UIColor.white
        masterCollectionView.allowsMultipleSelection = true
    }
}

extension MenuDetailVC {
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cvCellID, for: indexPath) as! CustomizeCell
        cell.customizable = masterCustomizables?[indexPath.section].choices?[indexPath.row]
        return cell
    }

    func numberOfSections(in collectionView: UICollectionView) -> Int {
        if let count = masterCustomizables?.count {
            return count
        } else {
            return 0
        }
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        if let count = masterCustomizables?[section].choices?.count {
            return count
        } else {
            return 0
        }
    }

The class MasterCustomize has an instance variable .isRequired that returns a bool, and if .isRequired == true, the section should contain single selection cells. Otherwise, it should contain multiple selection cells. Obviously, the current state of the program only returns multiple selection cells. Thank you!


Solution

  • In collectionView shouldSelectItemAt method check .isRequired value. If it is true check number of selected cells in that section. If existing selected cells count is 0, return true.

    If .isRequired value is false, return true

    func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
        if (masterCustomizables ?? [])[indexPath.section].isRequired {
            return collectionView.indexPathsForSelectedItems?.filter { $0.section == indexPath.section }.count == 0
        } else {
            return true
        }
    }
    

    Update

    If isRequired is true, deselect already selected cell programmatically.

    func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
        if (masterCustomizables ?? [])[indexPath.section].isRequired,
            let existingSelectedIndex = collectionView.indexPathsForSelectedItems?.first(where: { $0.section == indexPath.section }) {
                collectionView.deselectItem(at: existingSelectedIndex, animated: true)
        }
        return true
    }