I'm looking to add multiple drop shadows with different opacities to a view. The specs for the shadows are as follows:
I can get all this working just fine using CALayer
s. 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: .
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.