Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple Drop Shadows on Single View iOS

I'm looking to add multiple drop shadows with different opacities to a view. The specs for the shadows are as follows:

  • Y-offset of 4 with blur radius of 1
  • Y-offset of 10 with blur radius of 10
  • Y-offset of 2 with blur radius of 4
  • Blur radius of 1, spread of 1 (no offsets, will probably have to be 4 different shadows)

I can get all this working just fine using CALayers. Here's the code I have working for that (please note that I haven't bothered to set shadowPath yet, and won't until I get the multiple shadows thing working):

layer.cornerRadius = 4
layer.masksToBounds = false
layer.shouldRasterize = true
let layer2 = CALayer(layer: layer), layer3 = CALayer(layer: layer), layer4 = CALayer(layer: layer)
layer.shadowOffset = CGSizeMake(0, 4)
layer.shadowRadius = 1
layer2.shadowOffset = CGSizeMake(0, 10)
layer2.shadowRadius = 10
layer2.shadowColor = UIColor.blackColor().CGColor
layer2.shouldRasterize = true //Evidently not copied during initialization from self.layer
layer3.shadowOffset = CGSizeMake(0, 2)
layer3.shadowRadius = 4
layer3.shouldRasterize = true
layer4.shadowOffset = CGSizeMake(0, 1)
layer4.shadowRadius = 1
layer4.shadowOpacity = 0.1
layer4.shouldRasterize = true
layer.addSublayer(layer2)
layer.addSublayer(layer3)
layer.addSublayer(layer4)

(While this code is in Swift, I trust that it looks familiar enough to most Cocoa/Objective-C developers for it to make sense. Just know that layer is equivalent to self.layer in this context.)

The problem, however, arises when I attempt to use different opacities for each shadow. The shadowOpacity property of layer ends up being applied to all of its sublayers. This is a problem, as I need all of them to have their own shadow opacity. I have tried setting each layer's shadow opacity to its correct value (0.04, 0.12, etc.), but then the opacity of 0.04 of layer is applied to all sublayers. So I tried to set layer.shadowOpacity to 1.0, but this made all the shadows solid black. I also tried to be clever and do layer2.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.12).CGColor, but it was just changed to full black with no transparency.

I suppose it makes some sort of sense that the layers should all have the same shadow opacity. But what's a way to get this working, varying opacities and all (doesn't have to utilize CALayer if it's easier another way)?

Please don't answer with "just use an image": no matter how sane that may be, I'm trying to avoid it. Just humor me.

Thanks.

EDIT: As per request, here's what I'm after: Goal.

like image 682
Aehmlo Avatar asked Aug 12 '14 03:08

Aehmlo


1 Answers

The key thing that needs to be added is setting the layers' shadowPath. By default, Core Graphics draws a shadow around the layer's visible content, but in your code neither backgroundColor nor bounds are set for the layers, so the layers are actually empty.

Assuming you have a UIView subclass, you can make it work by adding something like this:

override func layoutSubviews() {
    super.layoutSubviews()
    layer.sublayers?.forEach { (sublayer) in
        sublayer.shadowPath = UIBezierPath(rect: bounds).cgPath
    }
}

I tested this approach on a view with multiple shadows and it worked as expected, as soon as the shadowPath is defined for the shadow layers. Different shadow colors and opacities worked as well, but you have to keep in mind that upper layers in the hierarchy will overlap the layers behind them, so if the front layer has a thick shadow, the other shadows can get hidden by it.

like image 147
maxkonovalov Avatar answered Nov 12 '22 13:11

maxkonovalov