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.
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.
Add subview with the same color which will be centered on the parent and will be with several pixels smaller. Like this you will have space from each side of the parent. On the parent turn on clipping subviews and add shadow to the inner view. Like this, you can have an inner shadow.
Go to the Storyboard. Add a Button to the main view and give it a title of "Shadow Tutorial". Select the Resolve Auto Layout Issues button and select Reset to Suggested Constraints. The Storyboard should look like this.
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 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
}
Wasabii's answer in 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
And in Swift 3/4/5:
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
Put this code in layoutSubviews() if you're using AutoLayout.
In SwiftUI, this is all much easier:
Color.yellow // or whatever your view
.shadow(radius: 3)
.frame(width: 200, height: 100)
The trick is defining the masksToBounds
property of your view's layer properly:
view.layer.masksToBounds = NO;
and it should work.
(Source)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With