123

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.

shim
  • 9,289
  • 12
  • 69
  • 108
Vincent Yiu
  • 1,291
  • 2
  • 11
  • 14
  • I think you need to improve your question to understand by others. It's really messy what u have asked – Dinesh Raja Nov 22 '12 at 04:49
  • ok updated, i just want to know how to add rounded corner and shadow to UICollectionViewCell – Vincent Yiu Nov 22 '12 at 06:12
  • Slight aside - use shadowRadius OR setShadowPath, no point in doing both as they are essentially the same instruction in this context (all rounded corners). A shadowpath is necessary where you want more complex shapes or rounded corners not applied to all corners. – Oly Dungey Aug 20 '14 at 12:35

17 Answers17

209

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
andrewtweber
  • 24,520
  • 22
  • 88
  • 110
Mike Sabatini
  • 2,541
  • 1
  • 17
  • 10
36

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
Bhaumik
  • 1,218
  • 1
  • 11
  • 20
Torre Lasley
  • 7,243
  • 3
  • 24
  • 12
  • I need to add a shadow effect to the right corner and bottom of each cell, and the way the code above works, it fills the cell with a shadowy color... – Joe Sene Jan 04 '17 at 11:34
28

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!

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
rocket101
  • 7,369
  • 11
  • 45
  • 64
26

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.

Md. Ibrahim Hassan
  • 5,359
  • 1
  • 25
  • 45
SmileBot
  • 19,393
  • 7
  • 65
  • 62
23

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
Massimo Polimeni
  • 4,826
  • 4
  • 27
  • 54
17

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.

Alexander
  • 1,424
  • 18
  • 23
16

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]];
Pang
  • 9,564
  • 146
  • 81
  • 122
  • 3
    This doesn't seem to work for me. I use similar code for a uitableviewcell but it doesnt seem to work in a collectionviewcell. It's weird how the same code wont work in the cv cell. – Jason Dec 11 '12 at 09:57
  • An update to my above comment. I found that UICollectionViewCells seem to use a different shadow offset than UITableViewCells. If you aren't seeing a shadow, try changing the offset (increasing the Y value helped me). – Jason Dec 12 '12 at 00:53
8

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];
Timothy Moose
  • 9,895
  • 3
  • 33
  • 44
  • 2
    Any idea why this only rounds my bottom corners? – user3344977 Jan 13 '15 at 19:34
  • Sounds like whatever layer you're rounding is partially obscured by something. Can't go any further than that without details. – Timothy Moose Jan 13 '15 at 19:37
  • Hey Tim, I just asked this question. I have a feeling you'll be able to get it quickly. Is it because I only have shadow on the bottom / "underneath" the cell? http://stackoverflow.com/q/27929735/3344977 – user3344977 Jan 13 '15 at 19:39
7

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.

Adam
  • 1,467
  • 1
  • 15
  • 15
6

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;
Bhaumik
  • 1,218
  • 1
  • 11
  • 20
Keith Holliday
  • 1,692
  • 21
  • 15
6

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
    }
}
marcelosalloum
  • 3,481
  • 4
  • 39
  • 63
4

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}
 }
3

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
Bhaumik
  • 1,218
  • 1
  • 11
  • 20
2

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
Bhaumik
  • 1,218
  • 1
  • 11
  • 20
Rahul Panzade
  • 1,302
  • 15
  • 12
2

I found an important thing: the UICollectionViewCell must have the backgroundColor as clear color in order to make these above work.

azun
  • 391
  • 4
  • 11
  • This is VERY important to achieve not cutting off shadow! Don't ignore this answer, thanks A LOT, man! – MarcinR Mar 08 '20 at 23:11
1

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.

Justin
  • 1,786
  • 20
  • 21
  • it doesn't work, clipsToBounds = false on the cell will cancel the corner radius. You need to apply the corner radius on the contentView and shadow on the cell itself – Quentin Rth Jan 26 '22 at 11:26
0
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
    }
}
Mihail Salari
  • 1,471
  • 16
  • 17