Je change la largeur d'un UITableViewCell afin que la cellule soit plus petite mais que l'utilisateur puisse toujours faire défiler le long des bords de la table.
override func layoutSubviews() {
// Set the width of the cell
self.bounds = CGRectMake(self.bounds.Origin.x, self.bounds.Origin.y, self.bounds.size.width - 40, self.bounds.size.height)
super.layoutSubviews()
}
Puis j'arrondis les coins:
cell.layer.cornerRadius = 8
cell.layer.masksToBounds = true
Tout va bien jusqu'à présent. Le problème se produit avec l'ombre. Les limites sont masquées, de sorte que l'ombre ne sera évidemment pas visible. J'ai cherché d'autres réponses mais je n'arrive pas à comprendre comment arrondir les angles le long des limites et montrer l'ombre.
cell.layer.shadowOffset = CGSizeMake(0, 0)
cell.layer.shadowColor = UIColor.blackColor().CGColor
cell.layer.shadowOpacity = 0.23
cell.layer.shadowRadius = 4
Ma question est donc: comment puis-je réduire la largeur, arrondir les angles et ajouter une ombre à un UITableViewCell en même temps?
Mise à jour: Essayer la réponse de R Moyer
Cette question arrive au bon moment! J'ai littéralement JUSTE résolu le même problème moi-même.
UIView
(appelons-la mainBackground
) dans la vue du contenu de votre cellule. Cela contiendra tout le contenu de votre cellule. Positionnez-le et appliquez les contraintes nécessaires dans le Storyboard.UIView
. Celui-ci sera celui avec l'ombre (appelons-le shadowLayer
). Positionnez-le exactement comme vous l'avez fait mainBackground
, mais derrière lui, et appliquez les mêmes contraintes.Vous devriez maintenant pouvoir définir les coins arrondis et les ombres comme suit:
cell.mainBackground.layer.cornerRadius = 8
cell.mainBackground.layer.masksToBounds = true
cell.shadowLayer.layer.masksToBounds = false
cell.shadowLayer.layer.shadowOffset = CGSizeMake(0, 0)
cell.shadowLayer.layer.shadowColor = UIColor.blackColor().CGColor
cell.shadowLayer.layer.shadowOpacity = 0.23
cell.shadowLayer.layer.shadowRadius = 4
Cependant, le problème est le suivant: le calcul de l'ombre pour chaque cellule est une tâche lente. Vous remarquerez un sérieux décalage lorsque vous faites défiler votre table. La meilleure façon de résoudre ce problème consiste à définir une UIBezierPath
pour l'ombre, puis à la pixelliser. Donc, vous voudrez peut-être faire ceci:
cell.shadowLayer.layer.shadowPath = UIBezierPath(roundedRect: cell.shadowLayer.bounds, byRoundingCorners: .AllCorners, cornerRadii: CGSize(width: 8, height: 8)).CGPath
cell.shadowLayer.layer.shouldRasterize = true
cell.shadowLayer.layer.rasterizationScale = UIScreen.mainScreen().scale
Mais cela crée un nouveau problème! La forme de UIBezierPath
dépend des limites de shadowLayer
, mais celles-ci ne sont pas correctement définies au moment où cellForRowAtIndexPath
est appelé. Vous devez donc ajuster la shadowPath
en fonction des limites de shadowLayer
. La meilleure façon de procéder consiste à sous-classer UIView
et à ajouter un observateur de propriété à la propriété bounds. Définissez ensuite toutes les propriétés de l'ombre dans didSet
. N'oubliez pas de changer la classe de votre shadowLayer
dans le storyboard afin qu'elle corresponde à votre nouvelle sous-classe.
class ShadowView: UIView {
override var bounds: CGRect {
didSet {
setupShadow()
}
}
private func setupShadow() {
self.layer.cornerRadius = 8
self.layer.shadowOffset = CGSize(width: 0, height: 3)
self.layer.shadowRadius = 3
self.layer.shadowOpacity = 0.3
self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: 8, height: 8)).cgPath
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
}
}
cell.layer.cornerRadius = 10
cell.layer.masksToBounds = true
Pour créer une ombre et un coin pour une cellule, vous n'avez besoin que d'une seule vue arrière. Voir mon exemple ci-dessous.
Vous devez ajouter backView
et définir leading
, trailing
, top
, bottom
contraintes égales à Content view
. Placez votre contenu sur backView
avec des contraintes appropriées, mais soyez sûr que votre contenu n'est pas masqué r backView
.
Après cela, dans votre code d'initialisation de cellule, ajoutez ces lignes:
override func awakeFromNib() {
super.awakeFromNib()
backgroundColor = Colors.colorClear
self.backView.layer.borderWidth = 1
self.backView.layer.cornerRadius = 3
self.backView.layer.borderColor = Colors.colorClear.cgColor
self.backView.layer.masksToBounds = true
self.layer.shadowOpacity = 0.18
self.layer.shadowOffset = CGSize(width: 0, height: 2)
self.layer.shadowRadius = 2
self.layer.shadowColor = Colors.colorBlack.cgColor
self.layer.masksToBounds = false
}
N'oubliez pas de créer IBOutlet pour Back View
.
Et voici le résultat:
La réponse acceptée fonctionne, mais l'ajout d'une sous-vue supplémentaire pour obtenir cet effet n'a guère de sens. Voici la solution qui fonctionne.
1ère étape : Ajouter l'ombre et le rayon de l'angle
// do this in one of the init methods
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// add shadow on cell
backgroundColor = .clear // very important
layer.masksToBounds = false
layer.shadowOpacity = 0.23
layer.shadowRadius = 4
layer.shadowOffset = CGSize(width: 0, height: 0)
layer.shadowColor = UIColor.blackColor().CGColor
// add corner radius on `contentView`
contentView.backgroundColor = .white
contentView.layer.cornerRadius = 8
}
2ème étape : Masque aux limites dans willDisplay
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
// this will turn on `masksToBounds` just before showing the cell
cell.contentView.layer.masksToBounds = true
}
Bonus : défilement lisse
// if you do not set `shadowPath` you'll notice laggy scrolling
// add this in `willDisplay` method
let radius = cell.contentView.layer.cornerRadius
cell.layer.shadowPath = UIBezierPath(roundedRect: cell.bounds, cornerRadius: radius).cgPath
J'ai obtenu le même résultat en utilisant le code suivant.Mais vous l'avez placé dans la méthode layoutSubviews () de votre sous-classe TableViewCell.
self.contentView.backgroundColor = [UIColor whiteColor];
self.contentView.layer.cornerRadius = 5;
self.contentView.layer.shadowOffset = CGSizeMake(1, 0);
self.contentView.layer.shadowColor = [[UIColor blackColor] CGColor];
self.contentView.layer.shadowRadius = 5;
self.contentView.layer.shadowOpacity = .25;
CGRect shadowFrame = self.contentView.layer.bounds;
CGPathRef shadowPath = [UIBezierPath bezierPathWithRect:shadowFrame].CGPath;
self.contentView.layer.shadowPath = shadowPath;
Une autre approche que vous pouvez essayer consiste à prendre une UIView
dans UITableViewCell
. Définissez la couleur de fond de UITableViewCell
pour effacer la couleur. Maintenant, vous pouvez créer des angles arrondis et ajouter des ombres sur UIVIew
. Cela apparaîtra comme si la largeur de la cellule était réduite et que l'utilisateur pouvait faire défiler le long des bords du tableau.
N'utilisez jamais UIBezierPath, bezierPathWithRect: shadowFrame, etc., car il est très lourd et dessine une couche au-dessus des vues. Il est nécessaire de recharger à nouveau la vue tabulaire pour vous assurer que les cellules s'affichent correctement et que, même, un rechargement peut ne pas aider. Utilisez plutôt un en-tête de section et un pied de page dont les bords sont arrondis selon les besoins et qui se trouvent également à l'intérieur du storyboard, ce qui facilitera le défilement et le chargement de la vue tableau sans aucun problème de rendu (parfois appelées cellules manquantes et elles apparaissent sur le défilement).
référez-vous ici à la définition des différentes valeurs entières des angles arrondis: Définition des angles masqués dans Interface Builder
Utilisez simplement les valeurs ci-dessus pour votre en-tête et votre pied de section.
Essayez ceci, cela a fonctionné pour moi.
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
Cela fonctionne sans vues supplémentaires!
override func awakeFromNib() {
super.awakeFromNib()
layer.masksToBounds = false
layer.cornerRadius = Constants.cornerRadius
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
let layer = cell.layer
layer.shadowOffset = CGSize(width: 0, height: 1)
layer.shadowRadius = 2
layer.shadowColor = UIColor.lightGray.cgColor
layer.shadowOpacity = 0.2
layer.frame = cell.frame
cell.tagLabel.text = tagItems[indexPath.row].text
return cell
}