Reputation: 1291
So I already went through various posts on adding 2nd view for adding shadow, but I still cannot get it to work if I want to add it in UICollectionViewCell
. I subclassed UICollectionViewCell
, and here is my code where I add various UI elements to the cell's content view and adding shadow to the layer:
[self.contentView setBackgroundColor:[UIColor whiteColor]];
self.layer.masksToBounds = NO;
self.layer.shadowOffset = CGSizeMake(0, 1);
self.layer.shadowRadius = 1.0;
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowOpacity = 0.5;
[self.layer setShadowPath:[[UIBezierPath bezierPathWithRect:self.bounds] CGPath]];
I would like to know how to add rounded corner and shadow to UICollectionViewCell
.
Upvotes: 123
Views: 120445
Reputation: 1581
extension UICollectionViewCell {
func shadowDecorate() {
self.layer.cornerRadius = 10
self.layer.borderWidth = 0.0
self.layer.borderColor = UIColor.lightGray.cgColor
self.layer.backgroundColor = UIColor.white.cgColor
self.layer.shadowColor = UIColor.gray.cgColor
self.layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
self.layer.shadowRadius = 3.0
self.layer.shadowOpacity = 0.5
self.layer.masksToBounds = false
}
}
Upvotes: 0
Reputation: 2076
Swift 5, Xcode 13, iOS 14
First config your collection as below:
self.collectionView.clipsToBounds = false
Then config your cell same as below:
override func awakeFromNib() {
super.awakeFromNib()
self.configView()
}
private func configView() {
self.clipsToBounds = false
self.backgroundColor = .systemBackground
self.layer.cornerRadius = 10
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 0, height: 0.0)
self.layer.shadowRadius = 10
self.layer.shadowOpacity = 0.2
}
Note to those two "clipToBounds = false" commands.
Just that.
Upvotes: 1
Reputation: 19652
Here's my answer, close to the others, but I add a corner radius to the layer otherwise the corners won't fill in correctly. Also, this makes a nice little extension on UICollectionViewCell
.
extension UICollectionViewCell {
func shadowDecorate() {
let radius: CGFloat = 10
contentView.layer.cornerRadius = radius
contentView.layer.borderWidth = 1
contentView.layer.borderColor = UIColor.clear.cgColor
contentView.layer.masksToBounds = true
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 1.0)
layer.shadowRadius = 2.0
layer.shadowOpacity = 0.5
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: radius).cgPath
layer.cornerRadius = radius
}
}
You can call it in collectionView(_:cellForItemAt:)
of the datasource once you dequeue your cell.
Upvotes: 28
Reputation: 431
I found an important thing: the UICollectionViewCell must have the backgroundColor as clear
color in order to make these above work.
Upvotes: 2
Reputation: 3531
I use the following method to accomplish this kind of effect:
extension UICollectionViewCell {
/// Call this method from `prepareForReuse`, because the cell needs to be already rendered (and have a size) in order for this to work
func shadowDecorate(radius: CGFloat = 8,
shadowColor: UIColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.3),
shadowOffset: CGSize = CGSize(width: 0, height: 1.0),
shadowRadius: CGFloat = 3,
shadowOpacity: Float = 1) {
contentView.layer.cornerRadius = radius
contentView.layer.borderWidth = 1
contentView.layer.borderColor = UIColor.clear.cgColor
contentView.layer.masksToBounds = true
layer.shadowColor = shadowColor.cgColor
layer.shadowOffset = shadowOffset
layer.shadowRadius = shadowRadius
layer.shadowOpacity = shadowOpacity
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: radius).cgPath
layer.cornerRadius = radius
}
}
Upvotes: 6
Reputation: 1477
Here's my take on the solution. It's similar to other answers, with one key difference. It doesn't create a path that's dependent on the bounds of the view. Anytime you create a path based on the bounds and provide it to the layer you can run into issues when it's resizing, and need to setup methods to update the path.
A simpler solution is to avoid using anything that is bounds dependent.
let radius: CGFloat = 10
self.contentView.layer.cornerRadius = radius
// Always mask the inside view
self.contentView.layer.masksToBounds = true
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 0, height: 1.0)
self.layer.shadowRadius = 3.0
self.layer.shadowOpacity = 0.5
// Never mask the shadow as it falls outside the view
self.layer.masksToBounds = false
// Matching the contentView radius here will keep the shadow
// in sync with the contentView's rounded shape
self.layer.cornerRadius = radius
Now when ever the cells size change the view API will do all the work internally.
Upvotes: 7
Reputation: 1504
SWIFT 4.2
One should add this in your custom cell or cellForItemAt: If you are using the cellForItemAt: approach substitute self -> cell
self.layer.cornerRadius = 10
self.layer.borderWidth = 1.0
self.layer.borderColor = UIColor.lightGray.cgColor
self.layer.backgroundColor = UIColor.white.cgColor
self.layer.shadowColor = UIColor.gray.cgColor
self.layer.shadowOffset = CGSize(width: 2.0, height: 4.0)
self.layer.shadowRadius = 2.0
self.layer.shadowOpacity = 1.0
self.layer.masksToBounds = false
This will give you a cell with a rounded border and a drop shadow.
Upvotes: 17
Reputation: 7537
In case it helps: Here is the swift for rounding the corners:
cell.layer.cornerRadius = 10
cell.layer.masksToBounds = true
with cell being a variable controlling the cell: often, you will use this in override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
Enjoy!
Upvotes: 28
Reputation: 7291
Swift 3 version:
cell.contentView.layer.cornerRadius = 10
cell.contentView.layer.borderWidth = 1.0
cell.contentView.layer.borderColor = UIColor.clear.cgColor
cell.contentView.layer.masksToBounds = true
cell.layer.shadowColor = UIColor.gray.cgColor
cell.layer.shadowOffset = CGSize(width: 0, height: 2.0)
cell.layer.shadowRadius = 2.0
cell.layer.shadowOpacity = 1.0
cell.layer.masksToBounds = false
cell.layer.shadowPath = UIBezierPath(roundedRect:cell.bounds, cornerRadius:cell.contentView.layer.cornerRadius).cgPath
Upvotes: 36
Reputation: 1372
You can set shadow color, radius and offset in UICollectionViewDataSource method while creating a UICollectionViewCell
cell.layer.shadowColor = UIColor.gray.cgColor
cell.layer.shadowOffset = CGSize(width: 0, height: 2.0)
cell.layer.shadowRadius = 1.0
cell.layer.shadowOpacity = 0.5
cell.layer.masksToBounds = false
Upvotes: 2
Reputation: 441
This one worked for me
cell.contentView.layer.cornerRadius = 5.0
cell.contentView.layer.borderColor = UIColor.gray.withAlphaComponent(0.5).cgColor
cell.contentView.layer.borderWidth = 0.5
let border = CALayer()
let width = CGFloat(2.0)
border.borderColor = UIColor.darkGray.cgColor
border.frame = CGRect(x: 0, y: cell.contentView.frame.size.height - width, width: cell.contentView.frame.size.width, height: cell.contentView.frame.size.height)
border.borderWidth = width
cell.contentView.layer.addSublayer(border)
cell.contentView.layer.masksToBounds = true
cell.contentView.clipsToBounds = true
Upvotes: 3
Reputation: 1732
I had to make some slight changes for Swift:
cell.contentView.layer.cornerRadius = 2.0;
cell.contentView.layer.borderWidth = 1.0;
cell.contentView.layer.borderColor = UIColor.clearColor().CGColor;
cell.contentView.layer.masksToBounds = true;
cell.layer.shadowColor = UIColor.grayColor().CGColor;
cell.layer.shadowOffset = CGSizeMake(0, 2.0);
cell.layer.shadowRadius = 2.0;
cell.layer.shadowOpacity = 1.0;
cell.layer.masksToBounds = false;
cell.layer.shadowPath = UIBezierPath(roundedRect:cell.bounds, cornerRadius:cell.contentView.layer.cornerRadius).CGPath;
Upvotes: 6
Reputation: 41
The answer of Mike Sabatini works fine, if you configure the cell properties directly on the collectionView cellForItemAt, but if you try to set them in awakeFromNib() of a custom UICollectionViewCell subclass, you will end with a wrong bezierPath set on the devices that doesn't match the width and height previously set in your Storyboard (IB).
Solution for me was create a func inside the subclass of UICollectionViewCell and calling it from the cellForItemAt like this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellID", for: indexPath) as? CustomCollectionViewCell{
cell.configure())
return cell
}
else {
return UICollectionViewCell()
}
}
And on the CustomCollectionViewCell.swift :
class CustomCollectionViewCell: UICollectionViewCell{
func configure() {
contentView.layer.cornerRadius = 20
contentView.layer.borderWidth = 1.0
contentView.layer.borderColor = UIColor.clear.cgColor
contentView.layer.masksToBounds = true
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 2.0)
layer.shadowRadius = 2.0
layer.shadowOpacity = 0.5
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: contentView.layer.cornerRadius).cgPath}
}
Upvotes: 4
Reputation: 4906
Here the Swift 4 solution, updated to round every corners and not only the top corners:
contentView.layer.cornerRadius = 6.0
contentView.layer.borderWidth = 1.0
contentView.layer.borderColor = UIColor.clear.cgColor
contentView.layer.masksToBounds = true
layer.shadowColor = UIColor.lightGray.cgColor
layer.shadowOffset = CGSize(width: 0, height: 2.0)
layer.shadowRadius = 6.0
layer.shadowOpacity = 1.0
layer.masksToBounds = false
layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: contentView.layer.cornerRadius).cgPath
layer.backgroundColor = UIColor.clear.cgColor
Upvotes: 23
Reputation: 2551
Neither of these solutions worked for me. If you place all your subviews into the UICollectionViewCell content view, which you probably are, you can set the shadow on the cell's layer and the border on the contentView's layer to achieve both results.
cell.contentView.layer.cornerRadius = 2.0f;
cell.contentView.layer.borderWidth = 1.0f;
cell.contentView.layer.borderColor = [UIColor clearColor].CGColor;
cell.contentView.layer.masksToBounds = YES;
cell.layer.shadowColor = [UIColor blackColor].CGColor;
cell.layer.shadowOffset = CGSizeMake(0, 2.0f);
cell.layer.shadowRadius = 2.0f;
cell.layer.shadowOpacity = 0.5f;
cell.layer.masksToBounds = NO;
cell.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:cell.bounds cornerRadius:cell.contentView.layer.cornerRadius].CGPath;
Swift 3.0
self.contentView.layer.cornerRadius = 2.0
self.contentView.layer.borderWidth = 1.0
self.contentView.layer.borderColor = UIColor.clear.cgColor
self.contentView.layer.masksToBounds = true
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 0, height: 2.0)
self.layer.shadowRadius = 2.0
self.layer.shadowOpacity = 0.5
self.layer.masksToBounds = false
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.contentView.layer.cornerRadius).cgPath
Upvotes: 210
Reputation: 169
Set the layer
attributes for the cell, not contentView
.
CALayer * layer = [cell layer];
[layer setShadowOffset:CGSizeMake(0, 2)];
[layer setShadowRadius:1.0];
[layer setShadowColor:[UIColor redColor].CGColor] ;
[layer setShadowOpacity:0.5];
[layer setShadowPath:[[UIBezierPath bezierPathWithRect:cell.bounds] CGPath]];
Upvotes: 16
Reputation: 9915
You simply need to (a) set cornerRadius
and (b) set shadowPath
to be a rounded rect with the same radius as cornerRadius
:
self.layer.cornerRadius = 10;
self.layer.shadowPath = [[UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.layer.cornerRadius] CGPath];
Upvotes: 8