Adrian
Adrian

Reputation: 16725

UICollectionViewCell rounded corners revert to square when breathed upon

I have a UICollectionView in a UIViewController. I use the following extension to configure the UICollectionViewCells to have rounded corners and shadows:

extension UICollectionViewCell {
    func configureCell() {
        self.contentView.layer.cornerRadius = 5.0
        self.contentView.layer.borderWidth = 1.0
        self.contentView.layer.borderColor = UIColor.clear.cgColor
        self.contentView.backgroundColor = UIColor.white
        self.contentView.layer.masksToBounds = true

        self.layer.shadowColor = UIColor.lightGray.cgColor
        self.layer.shadowOffset = CGSize.zero
        self.layer.shadowRadius = 2.0
        self.layer.shadowOpacity = 1.0
        self.layer.masksToBounds = false
        self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.contentView.layer.cornerRadius).cgPath
    }
}

When the UICollectionView is initially drawn, rounded corners and shadows are drawn on the cells. However, when I move the cells corners start disappearing, but the shadows remain. I figured I just need to update the cells, so I added this to moveItemAt:

func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {        
    [collectionView.cellForItem(at: sourceIndexPath), collectionView.cellForItem(at: destinationIndexPath)].forEach({$0?.configureCell()})
}

That didn't work, so I tried this:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    collectionView.visibleCells.forEach({$0.configureCell()})
}

Same result...rounded corners disappear, but shadows remain.

I welcome suggestions on how to keep corners rounded when UICollectionViewCells are moved. Thank you for reading.

Update:

Upon further tinkering, I've discovered any time the cells are clicked subsequent to the initial view loading, the rounded corners go square when the cell is clicked. So I tried this:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    collectionView.cellForItem(at: indexPath)?.configureCell()
}

func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
    collectionView.cellForItem(at: indexPath)?.configureCell()
}

Upvotes: 2

Views: 2723

Answers (2)

Adrian
Adrian

Reputation: 16725

I determined the problem was in my extension. I'm not 100% sure why it initially draws correctly and doesn't update, but I needed to take out the contentView reference and go straight to the layer.

I also took out all the references to configureCell() calls from the other methods. It works as desired. Here's what it looks like.

extension UICollectionViewCell {
    func configureCell() {
        self.layer.cornerRadius = 5.0
        self.layer.borderWidth = 1.0
        self.layer.borderColor = UIColor.clear.cgColor
        self.backgroundColor = UIColor.white
        self.layer.masksToBounds = true

        self.layer.shadowColor = UIColor.lightGray.cgColor
        self.layer.shadowOffset = CGSize.zero
        self.layer.shadowRadius = 2.0
        self.layer.shadowOpacity = 1.0
        self.layer.masksToBounds = false
        self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.contentView.layer.cornerRadius).cgPath
    }
}

Upvotes: 3

agibson007
agibson007

Reputation: 4373

My test are showing that it is working and I am not having any of your issues. I wonder where you are doing the initial configuration of the cell. My guess is that it is not in cellForItem but there may be something else. Here is my complete code and the collection view is loaded from storyboard with an identifier of "testCell"

import UIKit

    class ViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {

        @IBOutlet weak var collectionView: UICollectionView!
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
            collectionView.delegate = self
            collectionView.dataSource = self

            //horizontal

        }

        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }

        func numberOfSections(in collectionView: UICollectionView) -> Int {
            return 1
        }

        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return 100
        }

        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            //or cast to your cell
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "testCell", for: indexPath)
            //if you are dynamically sizing the cells I would call this last
            cell.configureCell()
            cell.layer.shouldRasterize = true
            cell.layer.rasterizationScale = UIScreen.main.scale
            return cell
        }

        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            return CGSize(width: 100, height: 100)
        }


        func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
            let cell = collectionView.cellForItem(at: indexPath)
            if let ourCell = cell{
                let highlightTest = UIView(frame: ourCell.bounds)
                highlightTest.layer.cornerRadius = ourCell.contentView.layer.cornerRadius
                highlightTest.backgroundColor = UIColor.lightGray
                highlightTest.layer.opacity = 0.4
                highlightTest.alpha = 0
                ourCell.contentView.addSubview(highlightTest)
                UIView.animate(withDuration: 0.3, delay: 0, options: .autoreverse, animations: {
                    highlightTest.alpha = 1
                }, completion: {
                    finished in
                    highlightTest.removeFromSuperview()
                })
            }
        }

        func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
            collectionView.deselectItem(at: indexPath, animated: true)
        }
    }



    extension UICollectionViewCell {
        func configureCell() {
            self.layer.cornerRadius = 5.0
            self.layer.borderWidth = 1.0
            self.layer.borderColor = UIColor.clear.cgColor
            self.contentView.backgroundColor = UIColor.white
            self.contentView.layer.masksToBounds = true

            self.layer.shadowColor = UIColor.lightGray.cgColor
            self.layer.shadowOffset = CGSize.zero
            self.layer.shadowRadius = 2.0
            self.layer.shadowOpacity = 1.0
            self.layer.masksToBounds = false
            self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.contentView.layer.cornerRadius).cgPath
        }
    }

Upvotes: 0

Related Questions