Why masksToBounds = YES prevents CALayer shadow?
Because shadow is an effect done outside the View, and that masksToBounds set to YES will tell the UIView not to draw anything that is outside itself.
If you want a roundedCorner view with shadow I suggest you do it with 2 views:
UIView *view1 = [[UIView alloc] init];
UIView *view2 = [[UIView alloc] init];
view1.layer.cornerRadius = 5.0;
view1.layer.masksToBounds = YES;
view2.layer.cornerRadius = 5.0;
view2.layer.shadowColor = [[UIColor blackColor] CGColor];
view2.layer.shadowOpacity = 1.0;
view2.layer.shadowRadius = 10.0;
view2.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
[view2 addSubview:view1];
[view1 release];
It's iOS 6 now, things might have changed. TheSquad's answer don't work for me until I managed to add one more line view2.layer.masksToBounds = NO;
, otherwise shadow doesn't show. Although documentation says masksToBounds
is NO by default, my code shows the opposite.
Here is how I make a rounded corner button with shadow, which is among the most commonly used code snippet in my app.
button.layer.masksToBounds = YES;
button.layer.cornerRadius = 10.0f;
view.layer.masksToBounds = NO; // critical to add this line
view.layer.cornerRadius = 10.0f;
view.layer.shadowOpacity = 1.0f;
// set shadow path to prevent horrible performance
view.layer.shadowPath =
[UIBezierPath bezierPathWithRoundedRect:_button.bounds cornerRadius:10.0f].CGPath;
[view addSubview:button];
EDIT
If views need to be animated or scrolled, masksToBounds = YES
tax performance significantly, which means animation will probably get stuttered. To get rounded corner and shadow AND smooth animation or scrolling, use following code instead:
button.backgroundColor = [UIColor clearColor];
button.layer.backgroundColor = [UIColor redColor].CGColor;
button.layer.masksToBounds = NO;
button.layer.cornerRadius = 10.0f;
view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:_button.bounds cornerRadius:10.0f].CGPath;
view.layer.shadowOffset = CGSizeMake(0.0f, 4.0f);
view.layer.shadowRadius = 2.0f;
view.layer.masksToBounds = NO;
view.layer.cornerRadius = 10.0f;
[view addSubview:button];
Swift 3.0 version with StoryBoard
The same idea with @TheSquad. Create a new view under the actual view and add shadow to the lower view.
1. Create a view under the actual view
Drag a UIView
to StoryBoard with same constraint as your target view. Check clip to bound for the target view. Also make sure the new view is listed before the target view so that the target view will cover the new view.
2. Now link the new view to your code add add shadow on it
This is just a sample. You can do whatever way you want here
shadowView.layer.masksToBounds = false
shadowView.layer.shadowColor = UIColor.red.cgColor
shadowView.layer.shadowOpacity = 0.5
shadowView.layer.shadowOffset = CGSize(width: -1, height: 1)
shadowView.layer.shadowRadius = 3
shadowView.layer.shadowPath = UIBezierPath(rect: coverImage.bounds).cgPath
shadowView.layer.shouldRasterize = true
This is the Swift 3 and IBDesignable version of the answer posted by @TheSquad.
I used the same concept while making changes in the storyboard file. First I moved my targetView (the one which requires corner radius and shadow) inside a new containerView. Then I added the following lines of code (Reference: https://stackoverflow.com/a/35372901/419192) to add some IBDesignable attributes for UIView Class:
@IBDesignable extension UIView {
/* The color of the shadow. Defaults to opaque black. Colors created
* from patterns are currently NOT supported. Animatable. */
@IBInspectable var shadowColor: UIColor? {
set {
layer.shadowColor = newValue!.cgColor
}
get {
if let color = layer.shadowColor {
return UIColor(cgColor: color)
}
else {
return nil
}
}
}
/* The opacity of the shadow. Defaults to 0. Specifying a value outside the
* [0,1] range will give undefined results. Animatable. */
@IBInspectable var shadowOpacity: Float {
set {
layer.shadowOpacity = newValue
}
get {
return layer.shadowOpacity
}
}
/* The shadow offset. Defaults to (0, -3). Animatable. */
@IBInspectable var shadowOffset: CGPoint {
set {
layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y)
}
get {
return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height)
}
}
/* The blur radius used to create the shadow. Defaults to 3. Animatable. */
@IBInspectable var shadowRadius: CGFloat {
set {
layer.shadowRadius = newValue
}
get {
return layer.shadowRadius
}
}
/* The corner radius of the view. */
@IBInspectable var cornerRadius: CGFloat {
set {
layer.cornerRadius = newValue
}
get {
return layer.cornerRadius
}
}
After adding this code, I went back to the storyboard and on selecting my containerView I could now find a new set of attributes in the attributes inspector:
Other than adding values for these attributes as per my choice, I also added a corner radius to my targetView and set the masksToBounds property as true.
I hope this helps :)