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

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.

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])!
}

Related

CollectionView didDeselectItemAt not getting called for Single selection in CollectionView

Following is my code for CollectionView
categoryCollectionView.delegate = self
categoryCollectionView.dataSource = self
categoryCollectionView.allowsSelection = true
categoryCollectionView.allowsMultipleSelection = false
Following is UICollectionViewCell code
class AppPageCategoryViewCell: UICollectionViewCell {
var catgory : String?
#IBOutlet weak var titleLbl: UILabel!
#IBOutlet weak var closeImageView: UIImageView!
#IBOutlet weak var stackContainer: UIView!
var facet : Facets?
func setUI() {
titleLbl.text = facet?.name ?? ""
let isSelected = facet?.isSelected ?? false
stackContainer.layer.borderColor = UIColor.black.cgColor
stackContainer.backgroundColor = isSelected ? UIColor.black : UIColor.white
titleLbl.textColor = isSelected ? UIColor.white : UIColor.black
closeImageView.image = closeImageView.image?.withRenderingMode(.alwaysTemplate)
closeImageView.tintColor = UIColor.hexStringToUIColor(hex: AppStrings.whiteColor)
stackContainer.layer.cornerRadius = 16
stackContainer.layer.borderWidth = 1
closeImageView.isHidden = !isSelected
}
}
Following is select deselct method
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
print("deselect----------deselect")
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("select----------select")
}
Now didDeselectItemAt is not getting called if I select one and item and select another item or if I select same item didDeselectItemAt not getting called at all it is just calling didSelectItemAt method why ? how to fix this?
This delegate method isn’t triggered because you set the Boolean “allowsMultipleSelection” to false. It needs to be true to allow the deselection of the item.
https://developer.apple.com/documentation/uikit/uitableview/1614938-allowsmultipleselection
EDIT:
I believe what you need to do here is to use the delegate method "shouldSelectItemAt" of the collectionView to check wether or not the cell being tapped on is already selected or not. If true, then you can deselect it, if false, do nothing.
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
if let currentIndexPathSelected = cellSelectedIndexPath, currentIndexPathSelected == indexPath {
collectionView.deselectItem(at: indexPath, animated: true)
collectionView.delegate?.collectionView?(collectionView, didDeselectItemAt: indexPath)
return false
}
return true
}
cellSelectedIndexPath is a private var to hold the current selected indexPath.
Calling the delegate will trigger the didDeselectItemAt method.
Let me know if that helps.

How to maintain state of collection view cell after dismissal of view?

So I have a collectionView that holds an array of trends users can click to feature that photo. When the cell is clicked, a checkmark appears letting the user know they have selected that category and their photoId is then entered into the selected childValues in the database.
Users have the option to edit their photo if they decided they want to remove their photo from a certain category. When I select edit profile, the cells that should be selected (ones I choose while uploading the photo) are unselected.
Or lets say for example, I already have a photo uploaded but now I want to feature it, when I go to edit photo and tap on a category the checkmark appears telling me this category is chosen. When I press save the photo is added to the chosen childValue in the database as expected, but when I click edit profile again and the view is presented. The cells I choose 10 seconds ago are now unselected.
How can I maintain a selected or deselected state even after dismissing the view controller?
var selectedCategoryValues = [String]()
var trend: Trend!
var selectedIndex = -1
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == trendCollectionView {
let trendsCell = collectionView.dequeueReusableCell(withReuseIdentifier:
"trendsCell", for: indexPath) as! TrendsCollectionCell
trendsCell.layer.cornerRadius = 9.0
trendsCell.layer.borderWidth = 0.1
trendsCell.layer.borderColor = UIColor.lightGray.cgColor
if selectedIndex == indexPath.row {
trendsCell.isSelected = true
} else {
trendsCell.isSelected = false
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView == trendCollectionView {
guard selectedIndex != indexPath.row else{return}
let indexpath = IndexPath(row:selectedIndex, section: 0)
trendCollectionView.cellForItem(at: indexpath)?.isSelected = !trendCollectionView.cellForItem(at: indexpath)!.isSelected
switch selectedSegmentIndex {
case 0: self.trend = femaleTrends[indexPath.row]
print("You selected \(trend.childValue)")
self.selectedCategoryValues.append(trend.childValue)
case 1: self.trend = maleTrends[indexPath.row]
print("You selected \(trend.childValue)")
self.selectedCategoryValues.append(trend.childValue)
default: break
}
Collection View Cell
class TrendsCollectionCell: UICollectionViewCell {
override var isSelected: Bool{
didSet{
if isSelected {
setSelectedUI()
}
else{
setUnSelectedUI()
}
}
}
func setSelectedUI(){
trendCheckmark.isHidden = false
trendCheckmark.tintColor = .white
}
func setUnSelectedUI(){
// reset to deafault, hide checkmark
trendCheckmark.isHidden = true
}
}
You can store the selected index as part of some state and then store / use that state as you wish. You'd do this by having some property selectedIndex, or, if you have more properties, you could have some struct viewState that you update in didSelectItemAt. You then either have some state manager outside of your view controllers (recommended) or you pass the state between view controllers (if you have a simple app).
Three needed steps for maintaining selected state of cell . This applicable for table view and collection view
1. Maintain last selected index for as selected cell .
2. Update cellindex on every did select call .
3. For every cellforrowindex method check if this selected cell or not.
Further explanation is on my previous answer
https://stackoverflow.com/questions/59293617/multiple-cells-selected-on-scrolling-reuse-cells-problem/
I never recommend using cell selected variable to detect cell selection because that variable is not completely describe the state , because of the dequeuing and recycling mechanism that work for UICollectionView and UITableView So I recommend Every time to rely all the state to your models ,
so you have 2 options
add an isSelcted property to every cell ModelObject
add Single property to your view Controller that reserve the selected Index
import Foundation
class NumberCountDataSource: NSObject , UICollectionViewDataSource {
let selectionAction: ((Int)->())
let numbersCount: Int
var selectedIndex: Int?
init(cardsCount: Int,selectionAction: #escaping ((Int)->())) {
self.numbersCount = cardsCount
self.selectionAction = selectionAction
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return numbersCount
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NumberCollectionViewCell.identiefier, for: indexPath) as! NumberCollectionViewCell
cell.configure(number: indexPath.row + 1, isSelected: indexPath.row == selectedIndex)
return cell
}
}
extension NumberCountDataSource: UICollectionViewDelegate , UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedIndex = indexPath.row
self.selectionAction(indexPath.row)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return CGFloat.init(0.0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return CGFloat.init(0.0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize.init(width: 60, height: 50)
}
}
class NumberCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var numberLable: UILabel!
#IBOutlet weak var backGroundview: AnimatableView!
static let identiefier: String = "NumberCollectionViewCell"
static let nibName: String = "NumberCollectionViewCell"
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override var isSelected: Bool {
didSet {
setupSelection(iseSelected: isSelected)
}
}
private func setupSelection(iseSelected: Bool) {
backGroundview.backgroundColor = isSelected ? UIColor("#4d8ec5") : UIColor("#ffffff")
backGroundview.borderColor = isSelected ? UIColor.clear : UIColor("#707070")
numberLable.textColor = isSelected ? UIColor("#ffffff") : UIColor("#444e53")
}
func configure(number: Int,isSelected: Bool) {
numberLable.text = "\(number)"
setupSelection(iseSelected: isSelected)
}
}

UICollectionView inside UITableViewCell returning empty always even though shows as selected

I am using a UICollectionView inside UITableViewCell. I am able to select the cells inside the UICollectionView. But when i try to get the UICollectionView or selected cells, the result is always null.I have been stuck on this for a long time. i included my code below for your reference.
class WeekDaysSelCell: UITableViewCell,UICollectionViewDelegate, UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {
var weekdays = ["S", "M", "T", "W", "T", "F", "S"]
var weekdaysSelected = [String]()
#IBOutlet var weeklyDaysColView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
self.weeklyDaysColView.delegate = self
self.weeklyDaysColView.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 7
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell : WeekDaysCollCell = weeklyDaysColView.dequeueReusableCell(withReuseIdentifier: "weekday", for: indexPath) as! WeekDaysCollCell
cell.weekDayLabel.text = weekdays[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell : WeekDaysCollCell = self.weeklyDaysColView.cellForItem(at: indexPath) as! WeekDaysCollCell
if (cell.backgroundColor == UIColor.gray) {
cell.backgroundColor = UIColor.clear
weekdaysSelected.removeAll { $0 == String(indexPath.row)}
//print("Removed from weekdaysSelected:", indexPath.row)
} else {
cell.backgroundColor = UIColor.gray
cell.isSelected = true
//weeklyDaysColView.selectItem(at: indexPath, animated: true, scrollPosition: [])
weekdaysSelected.append(String(indexPath.row))
//print("Added to weekdaysSelected:", indexPath.row)
}
}
}
// Trying to get the collection view from inside a willMove(toParent parent: UIViewController?) method.
override func willMove(toParent parent: UIViewController?) {
super.willMove(toParent: parent)
if parent == nil
{
if let delegate = self.delegate {
print("Inside If condition")
// Code that i use to get the cell
let cell3 = tableView.dequeueReusableCell(withIdentifier: "cell3") as! WeekDaysSelCell
print(cell3.weekdaysSelected)
print(cell3.weeklyDaysColView.indexPathsForSelectedItems)
// Trying to pass selected cells
//delegate.repeatCustomSelection(selectedIdx: String(lastSelection.row),repeatCustomSel: repeatCustomSelection)
}
}
}
You are trying to get a reusable cell in willMove(toParent parent: UIViewController?) , this is not going to return you a expected cell.
You need to get the cell , using a indexPath .
func cellForRow(at indexPath: IndexPath) -> UITableViewCell?
#andyPaul, is right you are generating the new cell in willMove(toParent parent: UIViewController?). Instead of that you have to pass the indexpath pf collection view when ever user selected any cell to your controller from the tableView Cell Class.
Now What is TypeAlias you can read from this link about type alias:- https://www.programiz.com/swift-programming/typealias
Create the typeAlias on above of your tableViewCell Class like this:-
typealias closureBlock = (_ isCapture : AnyObject?) ->()
class tableViewCellClass: UITableViewCell {
var callBack: closureBlock?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
Just Go to CollectionView didSelectItemAt Method and use this code after your coding
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell : WeekDaysCollCell = self.weeklyDaysColView.cellForItem(at: indexPath) as! WeekDaysCollCell
if (cell.backgroundColor == UIColor.gray) {
cell.backgroundColor = UIColor.clear
weekdaysSelected.removeAll { $0 == String(indexPath.row)}
//print("Removed from weekdaysSelected:", indexPath.row)
} else {
cell.backgroundColor = UIColor.gray
cell.isSelected = true
//weeklyDaysColView.selectItem(at: indexPath, animated: true, scrollPosition: [])
weekdaysSelected.append(String(indexPath.row))
//print("Added to weekdaysSelected:", indexPath.row)
}
guard let callBackClosure = self.callBack else {
return
}
callBackClosure(indexPath as AnyObject)
// You can pass any value here either indexpath or Array.
}
}
Now you have to initialise this closure so that it can check whether in which controller it will return the value when you assign the value from CollectionView didSelectItemAt Method.
Go to your ViewController Class where you have added the tableview and their datasources.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//You have to pass your tableview Cell Instance here and their reuse Identifier
let cell = tableView.dequeueReusableCell(withIdentifier: "tableViewCellClass", for: indexPath) as! tableViewCellClass
cell.callBack = { [weak self] (selectedIndexPath) -> ()in
// You will get the current selected index path of collection view here, Whenever you pass any index path from collectionView did SelectItem Method.
print(selectedIndexPath)
}
return cell
}

Make sure only 1 cell has an active state in a UICollectionView

I have an UICollectionView in which I want only want 1 cell to be active. With active I mean: the last cell that has been clicked (or the very first cell when to collection view lays out). When a user clicks a non-active cell, I want to reset the old active cell to a non-active state. I am having trouble doing this. This is because visibleCells, a property of collection view, only returns the cells on screen but not the cells in memory. This is my current way to locate an active cell and reset the state to non active.
This scenario can happen, causing multiple active cells: A user scroll slightly down so that the current active cell is not visible anymore, taps on a random cell and scroll up. The problem is that the old active cell stays in memory, although it is not visible: cellForItemAt(_:) does not gets called for that cell. Bad news is that visibleCells also do not find the old active cell. How can I find it? The function willDisplay cell also does not work.
An example project can be cloned directly into xCode: https://github.com/Jasperav/CollectionViewActiveIndex.
This is the code in the example project:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var collectionView: CollectionView!
static var activeIndex = 0
override func viewDidLoad() {
super.viewDidLoad()
collectionView.go()
}
}
class Cell: UICollectionViewCell {
#IBOutlet weak var button: MyButton!
}
class CollectionView: UICollectionView, UICollectionViewDelegate, UICollectionViewDataSource {
func go() {
delegate = self
dataSource = self
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 500
}
internal func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! Cell
if indexPath.row == ViewController.activeIndex {
cell.button.setTitle("active", for: .normal)
} else {
cell.button.setTitle("not active", for: .normal)
}
cell.button.addTarget(self, action: #selector(touchUpInside(_:)), for: .touchUpInside)
return cell
}
#objc private func touchUpInside(_ sender: UIButton){
let hitPoint = sender.convert(CGPoint.zero, to: self)
guard let indexPath = indexPathForItem(at: hitPoint), let cell = cellForItem(at: indexPath) as? Cell else { return }
// This is the problem. It does not finds the current active cell
// if it is just out of bounds. Because it is in memory, cellForItemAt: does not gets called
if let oldCell = (visibleCells as! [Cell]).first(where: { $0.button.titleLabel!.text == "active" }) {
oldCell.button.setTitle("not active", for: .normal)
}
cell.button.setTitle("active", for: .normal)
ViewController.activeIndex = indexPath.row
}
}
To recover from this glitch you can try in cellForRowAt
cell.button.tag = indexPath.row
when the button is clicked set
ViewController.activeIndex = sender.tag
self.reloadData()
You can use the isSelected property of the UIColectionViewCell. You can set an active layout to your cell if it is selected. The selection mechanism is implemented by default in the UIColectionViewCell. If you want to select/activate more than one cell you can set the property allowsMultipleSelection to true.
Basically this approach will look like this:
class ViewController: UIViewController {
#IBOutlet weak var collectionView: CollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.go()
}
func activeIndex()->Int?{
if let selectedItems = self.collectionView.indexPathsForSelectedItems {
if selectedItems.count > 0{
return selectedItems[0].row
}
}
return nil
}
}
class Cell: UICollectionViewCell {
#IBOutlet weak var myLabel: UILabel!
override var isSelected: Bool{
didSet{
if self.isSelected
{
myLabel.text = "active"
}
else
{
myLabel.text = "not active"
}
}
}
}
class CollectionView: UICollectionView, UICollectionViewDelegate, UICollectionViewDataSource {
func go() {
delegate = self
dataSource = self
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 500
}
internal func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! Cell
return cell
}
}

UICollectionView - Select all cells doesn't update properly

I have a button which selects all cells in the collectionview. Once clicked, the button function changes so that all cells will be de-selected upon pressing it again.
So far so good.. But
1) When you select all cells with the button, scroll a bit down and to the top again
2) Then de-select all cells with the button, and select all cells with the button again
3) And start scrolling down, some cells (mostly 1-2 complete rows, later cells are fine again) are not properly updated, so they don't appear with the selected state which is a different background color. Seems like an issue with dequeueReusableCell, but I can't wrap my head around it..
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if cell.isSelected {
cell.backgroundColor = UIColor.green
} else {
cell.backgroundColor = UIColor.white
}
if cell.viewWithTag(1) != nil {
let cellTitle = cell.viewWithTag(1) as! UILabel
cellTitle.text = String(indexPath.row)
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) {
cell.backgroundColor = UIColor.green
selectedCells.append(indexPath.row)
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) {
cell.backgroundColor = UIColor.white
selectedCells.removeObject(indexPath.row)
}
}
And the action method for handling button clicking
#IBAction func selectButtonTapped(_ sender: Any) {
if isSelectAllActive {
// Deselect all cells
selectedCells.removeAll()
for indexPath: IndexPath in collectionView!.indexPathsForSelectedItems! {
collectionView!.deselectItem(at: indexPath, animated: false)
collectionView(collectionView!, didDeselectItemAt: indexPath)
let cell: UICollectionViewCell
cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CVCell", for: indexPath)
}
selectButton.title = "Select all"
isSelectAllActive = false
} else {
// Select all cells
for i in 0 ..< collectionView!.numberOfItems(inSection: 0) {
collectionView!.selectItem(at: IndexPath(item: i, section: 0), animated: false, scrollPosition: UICollectionViewScrollPosition())
collectionView(collectionView!, didSelectItemAt: IndexPath(item: i, section: 0))
}
selectedCells.removeAll()
let indexPaths: [IndexPath] = collectionView.indexPathsForSelectedItems!
for item in indexPaths {
selectedCells.append(item.row)
}
selectedCells.sort{$0 < $1}
selectButton.title = "Select none"
isSelectAllActive = true
}
}
And for completion the array extension for removing an object
extension Array where Element : Equatable {
mutating func removeObject(_ object : Iterator.Element) {
if let index = self.index(of: object) {
self.remove(at: index)
}
}
}
Complete Xcode project can be found here: https://www.dropbox.com/s/uaj1asg43z7bl2a/SelectAllCells.zip
Used Xcode 9.0 beta 1, with iOS11 Simulator/iPhone SE
Thanks for your help!
Your code is a little confused because you are trying to keep track of cell selection state both in an array and in the cell itself.
I would just use a Set<IndexPath> as it is simpler and more efficient than an array. You can then refer to this set when returning a cell in cellForItemAt: and you don't need to do anything in willDisplay.
When you select/deselect all you can just reload the whole collection view and when an individual cell is selected/deselected, just reload that cell.
#objcMembers
class MainViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet var collectionView: UICollectionView!
#IBOutlet var toolBar: UIToolbar?
#IBOutlet weak var selectButton: UIBarButtonItem!
var selectedCells = Set<IndexPath>()
var isSelectAllActive = false
// MARK: - Classes
override func viewDidLoad() {
super.viewDidLoad()
// Collection view
collectionView!.delegate = self
collectionView!.dataSource = self
collectionView!.allowsMultipleSelection = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
}
#IBAction func selectButtonTapped(_ sender: Any) {
if isSelectAllActive {
// Deselect all cells
selectedCells.removeAll()
selectButton.title = "Select all"
isSelectAllActive = false
} else {
// Select all cells
for i in 0 ..< collectionView!.numberOfItems(inSection: 0) {
self.selectedCells.insert(IndexPath(item:i, section:0))
}
selectButton.title = "Select none"
isSelectAllActive = true
}
self.collectionView.reloadData()
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 50
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: UICollectionViewCell
cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CVCell", for: indexPath)
if self.selectedCells.contains(indexPath) {
cell.backgroundColor = .green
} else {
cell.backgroundColor = .white
}
if cell.viewWithTag(1) != nil {
let cellTitle = cell.viewWithTag(1) as! UILabel
cellTitle.text = String(indexPath.row)
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("\ndidSelectItemAt: \(indexPath.row)")
if selectedCells.contains(indexPath) {
selectedCells.remove(indexPath)
} else {
selectedCells.insert(indexPath)
}
self.collectionView.deselectItem(at: indexPath, animated: false)
self.collectionView.reloadItems(at: [indexPath])
print("selectedCells: \(selectedCells)")
}
}

Resources