J'essaie d'ajouter une ombre portée aux vues superposées, les vues se réduisent pour permettre de visualiser le contenu d'autres vues. Dans cet esprit, je souhaite conserver view.clipsToBounds
ON afin que les vues se réduisent. leur contenu est coupé.
Cela semble avoir rendu difficile l'ajout d'une ombre portée aux calques, car lorsque je tourne clipsToBounds
, les ombres sont également coupées.
J'ai essayé de manipuler view.frame
et view.bounds
afin d'ajouter une ombre portée au cadre, tout en permettant aux limites d'être assez grandes pour l'englober, mais je n'ai pas eu de chance avec cela.
Voici le code que j'utilise pour ajouter une ombre (cela ne fonctionne qu'avec clipsToBounds
OFF comme indiqué)
view.clipsToBounds = NO;
view.layer.shadowColor = [[UIColor blackColor] CGColor];
view.layer.shadowOffset = CGSizeMake(0,5);
view.layer.shadowOpacity = 0.5;
Voici une capture d'écran de l'ombre appliquée sur la couche grise la plus claire du haut. Espérons que cela donne une idée de la façon dont mon contenu se chevauchera si clipsToBounds
est désactivé.
Comment puis-je ajouter une ombre à mon UIView
et garder mon contenu coupé?
Edit: Je voulais juste ajouter que j’ai aussi joué avec l’utilisation d’images d’arrière-plan avec des ombres, ce qui fonctionne bien, mais je voudrais tout de même connaître la meilleure solution codée pour cela.
Essaye ça:
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:view.bounds];
view.layer.masksToBounds = NO;
view.layer.shadowColor = [UIColor blackColor].CGColor;
view.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = shadowPath.CGPath;
Tout d'abord: La UIBezierPath
utilisée comme shadowPath
est cruciale. Si vous ne l'utilisez pas, vous ne remarquerez peut-être pas de différence au début, mais l'œil averti observera un certain décalage se produisant lors d'événements tels que la rotation de l'appareil et/ou similaire. C'est une performance importante Tweak.
Concernant spécifiquement votre problème: La ligne importante est view.layer.masksToBounds = NO
. Il désactive l'écrêtage des sous-couches de la couche de la vue qui s'étendent au-delà des limites de la vue.
Pour ceux qui se demandent quelle est la différence entre masksToBounds
(sur le calque) et la propriété propre de la vue clipToBounds
: Il n'y en a pas vraiment. Basculer l'un aura un effet sur l'autre. Juste un niveau d'abstraction différent.
Swift 2.2:
override func layoutSubviews()
{
super.layoutSubviews()
let shadowPath = UIBezierPath(rect: bounds)
layer.masksToBounds = false
layer.shadowColor = UIColor.blackColor().CGColor
layer.shadowOffset = CGSizeMake(0.0, 5.0)
layer.shadowOpacity = 0.5
layer.shadowPath = shadowPath.CGPath
}
Swift 3:
override func layoutSubviews()
{
super.layoutSubviews()
let shadowPath = UIBezierPath(rect: bounds)
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0.0, height: 5.0)
layer.shadowOpacity = 0.5
layer.shadowPath = shadowPath.cgPath
}
La réponse de Wasabii dans Swift 2.3:
let shadowPath = UIBezierPath(rect: view.bounds)
view.layer.masksToBounds = false
view.layer.shadowColor = UIColor.blackColor().CGColor
view.layer.shadowOffset = CGSize(width: 0, height: 0.5)
view.layer.shadowOpacity = 0.2
view.layer.shadowPath = shadowPath.CGPath
Et dans Swift 3/4:
let shadowPath = UIBezierPath(rect: view.bounds)
view.layer.masksToBounds = false
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOffset = CGSize(width: 0, height: 0.5)
view.layer.shadowOpacity = 0.2
view.layer.shadowPath = shadowPath.cgPath
Placez ce code dans layoutSubviews () si vous utilisez AutoLayout.
L'astuce consiste à définir correctement la propriété masksToBounds
du calque de votre vue:
view.layer.masksToBounds = NO;
et ça devrait marcher.
( Source )
Vous pouvez créer une extension pour UIView pour accéder à ces valeurs dans l'éditeur de conception.
extension UIView{
@IBInspectable var shadowOffset: CGSize{
get{
return self.layer.shadowOffset
}
set{
self.layer.shadowOffset = newValue
}
}
@IBInspectable var shadowColor: UIColor{
get{
return UIColor(cgColor: self.layer.shadowColor!)
}
set{
self.layer.shadowColor = newValue.cgColor
}
}
@IBInspectable var shadowRadius: CGFloat{
get{
return self.layer.shadowRadius
}
set{
self.layer.shadowRadius = newValue
}
}
@IBInspectable var shadowOpacity: Float{
get{
return self.layer.shadowOpacity
}
set{
self.layer.shadowOpacity = newValue
}
}
}
Sur viewWillLayoutSubviews:
override func viewWillLayoutSubviews() {
sampleView.layer.masksToBounds = false
sampleView.layer.shadowColor = UIColor.darkGrayColor().CGColor;
sampleView.layer.shadowOffset = CGSizeMake(2.0, 2.0)
sampleView.layer.shadowOpacity = 1.0
}
Utilisation de l'extension de UIView:
extension UIView {
func addDropShadowToView(targetView:UIView? ){
targetView!.layer.masksToBounds = false
targetView!.layer.shadowColor = UIColor.darkGrayColor().CGColor;
targetView!.layer.shadowOffset = CGSizeMake(2.0, 2.0)
targetView!.layer.shadowOpacity = 1.0
}
}
Usage:
sampleView.addDropShadowToView(sampleView)
Alors oui, vous devriez préférer la propriété shadowPath pour les performances, mais aussi: À partir du fichier d'en-tête de CALayer.shadowPath
La spécification explicite du chemin en utilisant cette propriété * améliorera généralement les performances de rendu, tout comme le partage du même chemin * sur plusieurs couches.
Un truc moins connu consiste à partager la même référence sur plusieurs couches. Bien sûr, ils doivent utiliser la même forme, mais cela est courant avec les cellules de vue de table/collection.
Je ne sais pas pourquoi cela devient plus rapide si vous partagez des instances, je suppose qu'il met en cache le rendu de l'ombre et peut le réutiliser pour d'autres instances de la vue. Je me demande si c'est encore plus rapide avec