Adicionando canto arredondado e sombra projetada a UICollectionViewCell

104

Já passei por várias postagens sobre como adicionar uma 2ª visualização para adicionar sombra, mas ainda não consigo fazê-lo funcionar se quiser adicioná-lo UICollectionViewCell. Eu criei uma subclasse UICollectionViewCelle aqui está meu código onde adiciono vários elementos de IU à visualização do conteúdo da célula e adiciono sombra à camada:

[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]];

Gostaria de saber como adicionar cantos arredondados e sombras UICollectionViewCell.

Vincent Yiu
fonte
Eu acho que você precisa melhorar sua pergunta para entender por outras pessoas. É realmente confuso o que você perguntou
Dinesh Raja
ok atualizado, eu só quero saber como adicionar cantos arredondados e sombra a UICollectionViewCell
Vincent Yiu
Um pouco à parte - use shadowRadius OU setShadowPath, não adianta fazer os dois, pois são essencialmente a mesma instrução neste contexto (todos os cantos arredondados). Um caminho de sombra é necessário quando você deseja formas mais complexas ou cantos arredondados não aplicados a todos os cantos.
Oliver Dungey de

Respostas:

194

Nenhuma dessas soluções funcionou para mim. Se você colocar todas as suas subvisualizações na visão de conteúdo UICollectionViewCell, o que provavelmente é, você pode definir a sombra na camada da célula e a borda na camada de contentView para obter os dois resultados.

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
Mike Sabatini
fonte
9
Isso funcionou bem para mim depois que eu adicioneicell.layer.backgroundColor = [UIColor clearColor].CGColor;
nacross
2
Alguma ideia de por que isso só contorna meus cantos superiores?
user3344977
@ user3344977 talvez a parte inferior esteja sendo cortada? Como nos cantos são arredondados, mas está abaixo da parte visível da célula
CoderNinja
5
Acho que isso circunda meus cantos superiores e não os inferiores também.
fatuhoku
2
Como você faz com que os cantos permaneçam arredondados ao selecionar / desmarcar / mover células? Meus cantos ficam retos sempre que eu respiro na célula depois que ela é desenhada corretamente.
Adrian
32

Versão Swift 3:

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
Torre Lasley
fonte
Preciso adicionar um efeito de sombra no canto direito e na parte inferior de cada célula, e da forma como o código acima funciona, ele preenche a célula com uma cor sombreada ...
Joe Sene
25

Caso ajude: Aqui está a solução para arredondar os cantos:

cell.layer.cornerRadius = 10
cell.layer.masksToBounds = true

com a célula sendo uma variável que controla a célula: frequentemente, você vai usar isso em override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell

Aproveitar!

rocket101
fonte
18

Aqui, a solução Swift 4 , atualizada para arredondar todos os cantos e não apenas os cantos superiores:

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
fonte
1
Funciona em rápida 4,2 também, obrigado
ShadeToD
2
Eu estava procurando uma solução ... obrigado Massimo mais jovem: 'D
Massimo Polimeni
16

Defina os layeratributos da célula, não 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]];
inzonemobileDev
fonte
2
Isso não parece funcionar para mim. Eu uso um código semelhante para um uitableviewcell, mas não parece funcionar em um collectionviewcell. É estranho como o mesmo código não funciona na célula cv.
Jason
Uma atualização do meu comentário acima. Descobri que UICollectionViewCells parece usar um deslocamento de sombra diferente de UITableViewCells. Se você não estiver vendo uma sombra, tente alterar o deslocamento (aumentar o valor de Y me ajudou).
Jason
14

SWIFT 4.2

Deve-se adicionar isso em sua célula ou cellForItemAt customizada: Se você estiver usando 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

Isso lhe dará uma célula com uma borda arredondada e uma sombra projetada.

Alexandre
fonte
13

Aqui está minha resposta, perto das outras, mas adiciono um raio de canto à camada, caso contrário, os cantos não serão preenchidos corretamente. Além disso, isso é uma pequena extensão agradável 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
}

}

Você pode chamá-lo collectionView(_:cellForItemAt:)da fonte de dados depois de retirar a fila da célula.

smileBot
fonte
8

Você simplesmente precisa (a) definir cornerRadiuse (b) definir shadowPathcomo um retângulo arredondado com o mesmo raio de cornerRadius:

self.layer.cornerRadius = 10;
self.layer.shadowPath = [[UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.layer.cornerRadius] CGPath];
Timothy Moose
fonte
2
Alguma ideia de por que isso só contorna meus cantos inferiores?
user3344977
Parece que a camada que você está arredondando está parcialmente obscurecida por alguma coisa. Não posso ir além disso sem detalhes.
Timothy Moose
Ei Tim, acabei de fazer esta pergunta. Tenho a sensação de que você será capaz de obtê-lo rapidamente. É porque eu só tenho sombra na parte inferior / "embaixo" da célula? stackoverflow.com/q/27929735/3344977
user3344977
6

Tive que fazer algumas pequenas alterações no 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;
Keith Holliday
fonte
3

A resposta de Mike Sabatini funciona bem, se você configurar as propriedades da célula diretamente em collectionView cellForItemAt, mas se você tentar configurá-las em awakeFromNib () de uma subclasse UICollectionViewCell customizada, você terminará com um bezierPath incorreto configurado nos dispositivos que não não coincidir com a largura e altura previamente definidas em seu Storyboard (IB).

A solução para mim foi criar uma função dentro da subclasse de UICollectionViewCell e chamá-la de cellForItemAt assim:

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()
    }
}

E no 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}
 }
Frantenerelli
fonte
3

Este funcionou para mim

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
Tom Derry Marion Mantilla
fonte
3

Eu uso o seguinte método para conseguir esse tipo de efeito:

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
fonte
2

Você pode definir a cor da sombra, raio e deslocamento no método UICollectionViewDataSource ao criar um 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
Rahul Panzade
fonte
2

Aqui está minha opinião sobre a solução. É semelhante a outras respostas, com uma diferença fundamental. Ele não cria um caminho que depende dos limites da visualização. Sempre que você criar um caminho com base nos limites e fornecê-lo à camada, poderá ter problemas durante o redimensionamento e precisar configurar métodos para atualizar o caminho.

Uma solução mais simples é evitar o uso de qualquer coisa que seja dependente de limites.

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

Agora, sempre que o tamanho das células mudar, a API de visualização fará todo o trabalho internamente.

Adão
fonte
A resposta mais limpa na minha opinião. Adoro a ideia de manter os raios sincronizados.
Kirill Kudaev
2

Achei uma coisa importante: o UICollectionViewCell deve ter o backgroundColor como clearcor para que funcionem.

Khang Azun
fonte
Isso é MUITO importante para não eliminar a sombra! Não ignore essa resposta, MUITO obrigado, cara!
DennyDog