How to add a shadow to UICollectionViewCell with Compositional Layout?
Using a shadow for UICollectionView cells is a very common practice. Because collection view cells can change their size based on the content, it's important to apply and update shadow properly so that the shadow will be the same as the size of the cell.
As an example, we will use a collection view that uses UICollectionViewCompositionalLayout with an estimated cell height.
Task Flow: Task Manager App
Task Flow offers innovative features like multi-project tasks and the 'My Task Flow' concept. On top of the project structure, you get an endlessly flexible Task Flow view that suits the way you think and work. Available on iPhone, iPad, Mac and Apple Vision.
Try it now!
Configure shadow
First of all, we need a custom UICollectionViewCell that will have a rounded shape with a shadow:
class StatsCollectionViewCell: UICollectionViewCell {
private var cornerRadius: CGFloat = 14.0
override init(frame: CGRect) {
super.init(frame: frame)
configureLayout()
configureShadow()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
We can configure shadow in many different ways using the provided API:
private func configureShadow() {
// How blurred the shadow should be
layer.shadowRadius = 2
// How far the shadow is offset from the cell's frame
layer.shadowOffset = CGSize(width: 0, height: 2)
// The transparency of the shadow. Ranging from 0.0 (transparent) to 1.0 (opaque).
layer.shadowOpacity = 0.25
// The default color is black
layer.shadowColor = UIColor.black.cgColor
// To avoid the shadow to be clipped to the corner radius
layer.cornerRaduis = cornerRadius
layer.masksToBounds = false
}
For a better look, let's set the cell corner radius on init():
private func configureLayout() {
contentView.layer.cornerRadius = cornerRadius
contentView.layer.masksToBounds = true
contentView.backgroundColor = .secondarySystemGroupedBackground
}
Update shadowPath
Finally, we should specify the shadowPath to improve performance and update the size of the shadow if the cell bounds have changed. Shadow path should be updated in layoutSubviews():
override func layoutSubviews() {
super.layoutSubviews()
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath
}
Final result
In the end, I would like to show you the final result of our rounded cell with a shadow and visualize a problem that we can run into if we only set the shadowPath on init().
When constructing the NSCollectionLayoutSection we should use an estimated value for the height/width dimension in order to allow cells to be self-sized in that dimension. This way, the final size of the cell will be calculated when the content is rendered, so, it's important to update the shadowPath in layoutSubviews() as was shown before.
Here is the final result:

If you only set the
shadowPathoninit(), the shadow will use a height dimension that we provided as an estimated and won't be updated to the real content size (the shadow below is red for a better illustration):

Section Layout code sample
Here is a sample code of the section layout that I used in the example:
func createTotalCasesSection() -> NSCollectionLayoutSection {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(200))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(200))
let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 16, bottom: 10, trailing: 16)
return section
}
Task Flow: Task Manager App
Task Flow offers innovative features like multi-project tasks and the 'My Task Flow' concept. On top of the project structure, you get an endlessly flexible Task Flow view that suits the way you think and work. Available on iPhone, iPad, Mac and Apple Vision.
Try it now!
Conclusion
In this article, you've seen how to make a collection view cell with rounded corners and a shadow. Providing shadowPath is very important for performance optimization, however, you should use it properly for self-sizing cells in order to avoid unexpected results.
I hope you found this article useful and interesting, and if you did, feel free to share it with a friend or on social media. If you have any questions, suggestions, or feedback, please let me know on Twitter.
Thanks for reading!