Mommy
Mommy

Reputation: 103

How to change collectionview cells color based on device theme (following my color scheme)

Overview:

I'm building a keyboard Extension using collectionviews. I want the cells to change color based on the device theme (light/dark). At the moment, when I set the color scheme for my collectionview cells they don't work. I'm marking the problematic parts of my code with a "///" comment.

Resources:

I found this RayWenderlich project and I liked how they handled the color changing stuff so I copied it.

My code:

I have 3 classes:

  1. KeyboardViewController
  2. Custom View containing keyboard buttons
  3. Custom collectionview cells

CollectionView cell

class KeyboardKeys: UICollectionViewCell {
    
    var defaultColor = UIColor.white
    var highlighColor = UIColor.lightGray.withAlphaComponent(0.6)
    
    let label: UILabel = {
        let iv = UILabel()
        iv.translatesAutoresizingMaskIntoConstraints = false
        iv.contentMode =  .scaleAspectFit
        iv.font = UIFont.systemFont(ofSize: 20)
        iv.clipsToBounds = true
        iv.numberOfLines = 1
        iv.textAlignment = .center
        
        return iv
    }()

    override init(frame: CGRect) {
        super.init(frame: .zero)
        commonInit()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }
    
    func commonInit() {
        contentView.addSubview(label)
        label.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
        label.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
        label.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
        label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true   
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
            backgroundColor = isHighlighted ? highlighColor : defaultColor  
    }   
}

Custom View

class lettersKeyboard: UIView, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
var keyView: UICollectionView!
 let letters = ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"]
 override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
        
    }
    
    private func commonInit(){
//If you find some errors it's because this is way different in my code. This is just a regulare  collection view anyway

    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .vertical
        
    keyView = UICollectionView(frame: CGRect(x: 0.0, y: 0.0 , width: frame.width, height: 280), collectionViewLayout: layout)
    keyView.setCollectionViewLayout(layout, animated: true)
    keyView.isScrollEnabled = false
    keyView.register(KeyboardKeys.self, forCellWithReuseIdentifier: "collectionCellId")
    keyView.delegate = self
    keyView.dataSource = self
    addSubview(keyView)
}

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        10
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = keyView.dequeueReusableCell(withReuseIdentifier: "collectionCellId", for: indexPath) as! KeyboardKeys
        cell.label.text = letters[indexPath.row]
        return cell
    }
    ///I guess something is wrong here 
    func setColorScheme(_ colorScheme: ColorScheme) {
      let colorScheme = CColors(colorScheme: colorScheme)
     
      for view in subviews {
        if let cell = view as? KeyboardKeys {
            cell.tintColor = colorScheme.buttonTextColor
            cell.defaultColor = colorScheme.keysDefaultColor
            cell.highlighColor = colorScheme.keysHighlightColor
      }
      }
    }
    
}

Color scheme struct

enum ColorScheme {
    case dark
    case light
}

struct CColors {
    
    let keysDefaultColor: UIColor
    let keysHighlightColor: UIColor
    
    let buttonTextColor: UIColor
   
    
    init(colorScheme: ColorScheme) {
        switch colorScheme {
        case .light:
           
            keysDefaultColor = .systemRed
                //UIColor.white
            keysHighlightColor = UIColor.lightGray.withAlphaComponent(0.6)
         
            buttonTextColor = .black
           
        case .dark:
            
            keysDefaultColor = .systemBlue
                // UIColor.gray.withAlphaComponent(0.5)
            keysHighlightColor = UIColor.lightGray.withAlphaComponent(0.5)
            
            buttonTextColor = .white  
        }
    }
}

KeyboardViewController

class KeyboardViewController: UIInputViewController {
var letters : lettersKeyboard = {
      
        let m = lettersKeyboard(frame: .zero)
            m.translatesAutoresizingMaskIntoConstraints = false

            m.backgroundColor = .clear
            return m
        
}()
override func viewDidLoad() {
        super.viewDidLoad()
 
        view.addSubview(letters)
   
        letters.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        letters.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        letters.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        letters.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
//The rest is the default inputvc stuff 

    ///Or here
    override func textDidChange(_ textInput: UITextInput?) {
        // The app has just changed the document's contents, the document context has been updated.
       
        let colorScheme:  ColorScheme
        let proxy = self.textDocumentProxy
        if proxy.keyboardAppearance == UIKeyboardAppearance.dark {
            colorScheme = .dark
        } else {
            colorScheme = .light
        }
        letters.setColorScheme(colorScheme)
    }
}

Question:

I don't know what I'm doing wrong since my code works with everything except for collectionview cells. I guess another way of doing this stuff exists. So how do I change my collectionView cells' color based on the device's theme following my color scheme?

Upvotes: 1

Views: 613

Answers (2)

Mommy
Mommy

Reputation: 103

A very kind guy helped me out and found this solution. The problem here is that I forgot the view's hierarchy.

CollectionView cell

 override func layoutSubviews() {
        super.layoutSubviews()
           setupBackGround()
    } 
func setupBackGround(){
backgroundColor = isHighlighted ? highlighColor : defaultColor
}

KeyboardViewController


func setColorScheme(_ colorScheme: ColorScheme) {
      let colorScheme = CColors(colorScheme: colorScheme)
     
      for view in subviews {
         func setToRootView(view: UIView) {
                if let cell = view as? KeyboardKeys {
                    cell.tintColor = colorScheme.buttonTextColor
                    cell.defaultColor = colorScheme.keysDefaultColor
                    cell.highlighColor = colorScheme.keysHighlightColor
                    cell.setBackground()
                    return
                }
                guard view.subviews.count > 0 else {
                    return
                }
                view.subviews.forEach(setToRootView(view:))
            }
            setToRootView(view: self)
        }

Upvotes: 0

Jay
Jay

Reputation: 2661

You should really be reloading the collection view, rather than trying to find the subviews that are the keys, and updating those.

Pass in the colorScheme model to each cell and have the colors be set as a result of a reload.

Upvotes: 2

Related Questions