web-dev-qa-db-fra.com

Quelle est la meilleure façon d'ajouter une ombre portée à mon UIView

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é.

Shadow Application.

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.

103
Wez

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
}
272
pkluz

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.

62
Bart van Kuik

L'astuce consiste à définir correctement la propriété masksToBounds du calque de votre vue:

view.layer.masksToBounds = NO;

et ça devrait marcher.

( Source )

13
sergio

Vous pouvez créer une extension pour UIView pour accéder à ces valeurs dans l'éditeur de conception.

Shadow options in design editor

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
        }
    }
}
13
Chris Stillwell

Vous pouvez également définir des ombres sur votre vue à partir du storyboard.

enter image description here

6
pallavi

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)
1
A.G

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

0
Rufus Mall