Home > Mobile >  Changing Color for CollectionView Cells in Collection View
Changing Color for CollectionView Cells in Collection View

Time:06-08

I have a collection view that looks to a string array.

I'm looking to solve these questions related to the color of the View:

  1. On Load, I'd like the cell "All Venues" to be highlighted blue/white font while the remaining cells to be their default white with black text.

  2. Upon click of an additional cell, I'd like all cells including "All Venues" to change to default and the cell selected to be blue/white. If a cell has already been selected previously and I choose another cell, I'd like this previous to default.

Here is my code:

//Variables
var selectedItems: [String] = []
var selectedItem = String()
var previousIndex = 0

//Collection View Functions
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    
    let cell = collectionView.cellForItem(at: indexPath) as! FilterCollectionViewCell
    let newSelectedItem = collectionViewArray[indexPath.row].name ?? ""
    let previousIndexPath = IndexPath(index: previousIndex)
    
    if selectedItem == newSelectedItem {
        //if the same item, nothing happens
    } else if selectedItem != newSelectedItem && cell.isHighlightedCell == false {
        if previousIndex != nil {
         //this is what I tried, if the item has already been selected, I try to reference that previous cell with that cell's indexPath
            let previousCell = collectionView.cellForItem(at: previousIndexPath) as! FilterCollectionViewCell
            previousCell.isHighlightedCell = false
            previousCell.filterContainerView.backgroundColor = .none
            previousCell.nameLabel.textColor = .black
        }
        //Changes are Made
        cell.isHighlightedCell = true
        selectedItems.append(selectedItem ?? "")
        cell.filterContainerView.backgroundColor = UIColor(hexString: "001A38")
        cell.nameLabel.textColor = .white
    }
    
    if newSelectedItem != "All Venues" {
        
        searchIsActive = true
        selectedItems.removeAll()
    } else {
        searchIsActive = false
    }
    
    //changes Selection
    selectedItem = newSelectedItem
    previousIndex = indexPath.row
    
    //reload/animation
    self.barsShown = barArray.filter{ $0.type == selectedItem}
    barDisplayedTableView.reloadData()
    updateAnimationForTableView()
    
}

  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FilterCollectionViewCell", for: indexPath) as! FilterCollectionViewCell
    //Tried to implement this for All Venues as default
    if indexPath.row == 0 && indexPath.row == previousIndex {
        cell.filterContainerView.backgroundColor = UIColor(hexString: "001A38")
        cell.nameLabel.textColor = .white
    } 
    //if true, it displays this color
    if cell.isHighlightedCell == true {
        cell.filterContainerView.backgroundColor = UIColor(hexString: "001A38")
        cell.nameLabel.textColor = .white
    } else {
        cell.filterContainerView.backgroundColor = .none
        cell.nameLabel.textColor = .black
    }

    return cell
    
}

CodePudding user response:

You can save yourself a lot of work by letting the cell class handle its own selected / unselected appearance.

So, if we have a simple cell class with a label:

class LabelCollectionViewCell: UICollectionViewCell {
    let label = UILabel()
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() {
        label.textAlignment = .center
        label.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(label)
        let g = contentView.layoutMarginsGuide
        NSLayoutConstraint.activate([
            label.topAnchor.constraint(equalTo: g.topAnchor, constant: 4.0),
            label.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0),
            label.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
            label.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -4.0),
        ])
        
        // default (unselected) appearance
        contentView.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
        label.textColor = .black
        
        // let's round the corners so it looks nice
        contentView.layer.cornerRadius = 12
    }
}

and we want this difference between selected / unselected:

enter image description here

enter image description here

we can override isSelected in that cell class:

override var isSelected: Bool {
    didSet {
        contentView.backgroundColor = isSelected ? .systemBlue : UIColor(white: 0.95, alpha: 1.0)
        label.textColor = isSelected ? .white : .black
    }
}

Now, the color changes - including changing the currently selected cell back to the "default" colors - is fully automatic.

And, because the collection view manages the selection state internally, we don't even need to do anything in cellForItemAt ... even if we've scrolled the selected cell out-of-view.

Here's a quick, complete example:

class LabelCollectionViewCell: UICollectionViewCell {
    let label = UILabel()
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() {
        label.textAlignment = .center
        label.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(label)
        let g = contentView.layoutMarginsGuide
        NSLayoutConstraint.activate([
            label.topAnchor.constraint(equalTo: g.topAnchor, constant: 4.0),
            label.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0),
            label.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
            label.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -4.0),
        ])
        
        // default (unselected) appearance
        contentView.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
        label.textColor = .black
        
        // let's round the corners so it looks nice
        contentView.layer.cornerRadius = 12
    }
    override var isSelected: Bool {
        didSet {
            contentView.backgroundColor = isSelected ? .systemBlue : UIColor(white: 0.95, alpha: 1.0)
            label.textColor = isSelected ? .white : .black
        }
    }
}

class SelColViewVC: UIViewController {
    
    var collectionView: UICollectionView!
    
    let myData: [String] = [
        "All Venues",
        "Venue One",
        "Venue Two",
        "Venue Three",
        "Venue Four",
        "Venue Five",
        "Venue Six",
        "Venue Seven",
        "Venue Eight",
    ]
    
    // use this to track the currently selected item / cell
    var currentSelection: IndexPath!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let fl = UICollectionViewFlowLayout()
        fl.scrollDirection = .horizontal
        fl.estimatedItemSize = CGSize(width: 120, height: 50)
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: fl)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(collectionView)
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            collectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            collectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            collectionView.heightAnchor.constraint(equalToConstant: 80.0),
        ])
        
        collectionView.contentInset = UIEdgeInsets(top: 0.0, left: 8.0, bottom: 0.0, right: 8.0)
        collectionView.register(LabelCollectionViewCell.self, forCellWithReuseIdentifier: "cell")
        collectionView.dataSource = self
        collectionView.delegate = self
        
        // we want to "pre-select" the first item / cell
        let idx = IndexPath(item: 0, section: 0)
        collectionView.selectItem(at: idx, animated: false, scrollPosition: .left)

        // update currentSelection var
        currentSelection = idx
    }
    
}
extension SelColViewVC: UICollectionViewDataSource, UICollectionViewDelegate {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return myData.count
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let c = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! LabelCollectionViewCell
        c.label.text = myData[indexPath.item]
        return c
    }
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        if currentSelection == indexPath {
            // user tapped already selected cell, so
            //  just return
            print("Tapped Already Selected item:", indexPath.item)
            return
        }

        // update currentSelection var
        currentSelection = indexPath
        
        print("New Selected item:", indexPath.item)
        // run code for new selection
    }
}
  • Related