Wezly Wezly - 2 months ago 19
iOS Question

Whats the best way to add a drop shadow to my UIView

I am trying to add a drop shadow to views that are layered on top of one another, the views collapse allowing content in other views to be seen, in this vein i want to keep

view.clipsToBounds
ON so that when the views collapse their content is clipped.

This seems to have made it difficult for me to add a drop shadow to the layers as when i turn
clipsToBounds
ON the shadows are clipped also.

I have been trying to manipulate
view.frame
and
view.bounds
in order to add a drop shadow to the frame but allow the bounds to be large enough to encompass it, however I have had no luck with this.

Here is the code I am using to add a Shadow (this only works with
clipsToBounds
OFF as shown)

view.clipsToBounds = NO;
view.layer.shadowColor = [[UIColor blackColor] CGColor];
view.layer.shadowOffset = CGSizeMake(0,5);
view.layer.shadowOpacity = 0.5;


Here is a screenshot of the shadow being applied to the top lightest grey layer. Hopefully this gives an idea of how my content will overlap if
clipsToBounds
is OFF.

Shadow Application.

How can I add a shadow to my
UIView
and keep my content clipped?

Edit: Just wanted to add that I have also played around with using background images with shadows on, which does work well, however I would still like to know the best coded solution for this.

Answer

Try this:

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;

First of all: The UIBezierPath used as shadowPath is crucial. If you don't use it, you might not notice a difference at first, but the keen eye will observe a certain lag occurring during events like rotating the device and/or similar. It's an important performance tweak.

Regarding your issue specifically: The important line is view.layer.masksToBounds = NO. It disables the clipping of the view's layer's sublayers that extend further than the view's bounds.

For those wondering what the difference between masksToBounds (on the layer) and the view's own clipToBounds property is: There isn't really any. Toggling one will have an effect on the other. Just a different level of abstraction.


Swift version to be used with autolayout:

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
}