UIView with rounded corners and drop shadow?
Solution 1
The following code snippet adds a border, border radius, and drop shadow to v
, a UIView
:
// border radius
[v.layer setCornerRadius:30.0f];
// border
[v.layer setBorderColor:[UIColor lightGrayColor].CGColor];
[v.layer setBorderWidth:1.5f];
// drop shadow
[v.layer setShadowColor:[UIColor blackColor].CGColor];
[v.layer setShadowOpacity:0.8];
[v.layer setShadowRadius:3.0];
[v.layer setShadowOffset:CGSizeMake(2.0, 2.0)];
Swift 5 Version :
// border radius
v.layer.cornerRadius = 30.0
// border
v.layer.borderColor = UIColor.lightGray.cgColor
v.layer.borderWidth = 1.5
// drop shadow
v.layer.shadowColor = UIColor.black.cgColor
v.layer.shadowOpacity = 0.8
v.layer.shadowRadius = 3.0
v.layer.shadowOffset = CGSize(width: 2.0, height: 2.0)
You can adjust the settings to suit your needs.
Also, add the QuartzCore framework to your project and:
#import <QuartzCore/QuartzCore.h>
See my other answer regarding masksToBounds
.
Note
This may not work in all cases. If you find that this method interferes with other drawing operations that you are performing, please see this answer.
Solution 2
Swift
// corner radius
blueView.layer.cornerRadius = 10
// border
blueView.layer.borderWidth = 1.0
blueView.layer.borderColor = UIColor.black.cgColor
// shadow
blueView.layer.shadowColor = UIColor.black.cgColor
blueView.layer.shadowOffset = CGSize(width: 3, height: 3)
blueView.layer.shadowOpacity = 0.7
blueView.layer.shadowRadius = 4.0
Exploring the options
Problem 1: Shadow gets clipped off
What if there are sublayers or subviews (like an image) whose content we want to clip to the bounds of our view?
We can accomplish this with
blueView.layer.masksToBounds = true
(Alternatively, blueView.clipsToBounds = true
gives the same result.)
But, oh no! The shadow was also clipped off because it's outside of the bounds! What to do? What to do?
Solution
Use separate views for the shadow and the border. The base view is transparent and has the shadow. The border view clips any other subcontent that it has to its borders.
// add the shadow to the base view
baseView.backgroundColor = UIColor.clear
baseView.layer.shadowColor = UIColor.black.cgColor
baseView.layer.shadowOffset = CGSize(width: 3, height: 3)
baseView.layer.shadowOpacity = 0.7
baseView.layer.shadowRadius = 4.0
// add the border to subview
let borderView = UIView()
borderView.frame = baseView.bounds
borderView.layer.cornerRadius = 10
borderView.layer.borderColor = UIColor.black.cgColor
borderView.layer.borderWidth = 1.0
borderView.layer.masksToBounds = true
baseView.addSubview(borderView)
// add any other subcontent that you want clipped
let otherSubContent = UIImageView()
otherSubContent.image = UIImage(named: "lion")
otherSubContent.frame = borderView.bounds
borderView.addSubview(otherSubContent)
This gives the following result:
Problem 2: Poor performance
Adding rounded corners and shadows can be a performance hit. You can improve performance by using a predefined path for the shadow and also specifying that it be rasterized. The following code can be added to the example above.
baseView.layer.shadowPath = UIBezierPath(roundedRect: baseView.bounds, cornerRadius: 10).cgPath
baseView.layer.shouldRasterize = true
baseView.layer.rasterizationScale = UIScreen.main.scale
See this post for more details. See here and here also.
This answer was tested with Swift 4 and Xcode 9.
Solution 3
Check out the example project on GitHub to make sure you use the component correctly.
Simple Swift 5 solution without any additional subviews or subclassing:
extension UIView {
func addShadow(offset: CGSize, color: UIColor, radius: CGFloat, opacity: Float) {
layer.masksToBounds = false
layer.shadowOffset = offset
layer.shadowColor = color.cgColor
layer.shadowRadius = radius
layer.shadowOpacity = opacity
let backgroundCGColor = backgroundColor?.cgColor
backgroundColor = nil
layer.backgroundColor = backgroundCGColor
}
}
Note that you should set up your view with corner radius and other properties before calling addShadow
.
After that, just call this from viewDidLoad
like this:
button.addShadow(offset: CGSize.init(width: 0, height: 3), color: UIColor.black, radius: 2.0, opacity: 0.35)
Final result:
Super easy and simple!
Solution 4
One way to do this is to put the view with rounded corners in a view with the drop shadow.
UIView* roundedView = [[UIView alloc] initWithFrame: frame];
roundedView.layer.cornerRadius = 5.0;
roundedView.layer.masksToBounds = YES;
UIView* shadowView = [[UIView alloc] initWithFrame: frame];
shadowView.layer.shadowColor = [UIColor blackColor].CGColor;
shadowView.layer.shadowRadius = 5.0;
shadowView.layer.shadowOffset = CGSizeMake(3.0, 3.0);
shadowView.layer.shadowOpacity = 1.0;
[shadowView addSubview: roundedView];
Then you can add the shadowView wherever you want.
Solution 5
This worked for me. Trick was to move the background color from the main view to the layer.
CALayer *layer = view.layer;
layer.cornerRadius = 15.0f;
layer.masksToBounds = NO;
layer.shadowOffset = CGSizeMake(0, 3);
layer.shadowColor = [[UIColor blackColor] CGColor];
layer.shadowRadius = 2.0f;
layer.shadowOpacity = 0.35f;
layer.shadowPath = [[UIBezierPath bezierPathWithRoundedRect:layer.bounds cornerRadius:layer.cornerRadius] CGPath];
CGColorRef bColor = view.backgroundColor.CGColor;
view.backgroundColor = nil;
layer.backgroundColor = bColor ;
Aditya Vaidyam
Updated on July 21, 2022Comments
-
Aditya Vaidyam almost 2 years
I’ve been working on an application for a couple of years and received a simple design request: Round the corners on a UIView and add a drop shadow.To do as given below.
I want a custom
UIView
... : I just wanted a blank white view with rounded corners and a light drop shadow (with no lighting effect). I can do each of those one by one but the usualclipToBounds
/maskToBounds
conflicts occur. -
Bhavin Bhadani about 7 years@Suragch so for only border, we need to use separate view or separate views for corner radius and shadow too?
-
Suragch about 7 years@EICaptainv2.0, If you only want a border (and/or corner radius) then you don't need a separate view. The separate view is for the situation where you need round corners and shadow.
-
Rutger Huijsmans about 7 yearsThis isn't working for me. When I set the back ground color to clear on the baseView a shadow does not appear anymore. What am I doing wrong?
-
Cesare almost 7 yearsDoes this work on buttons? Because it's not working on my end.
-
Hemang over 6 yearsI tried following the exact steps you suggested. But still no luck. It would be great if you share a sample (on Github) to see how you have done which seems impossible for me and other people.
-
trupin over 6 yearsManaged to make it work only by removing this line
layer.shadowPath = UIBezierPath.init(roundedRect: layer.bounds, cornerRadius: layer.cornerRadius).cgPath
. Can't explain why though, does someone has an explanation for that? -
Aleksander over 6 yearsNot working, setting
baseView.backgroundColor = UIColor.clear
removes the shadow. Only if you set a background color will you see it. -
Suragch over 6 years@Markus, I retested the code in my answer and it still worked with Swift 4 and Xcode 9.
-
Suragch over 6 years@Aleksander, I retested the code in my answer and it still worked with Swift 4 and Xcode 9. Even with the clear background color on the base view it still worked. I'm not sure why it wasn't working for you.
-
Mike Vosseller over 6 yearsFYI I was initially seeing the same problem that other commenters were seeing where the baseView's shadow not displaying when it's background color was clear. The problem was that I was only running the first part of the code (the baseView stuff). Once I added the borderView as a subview the shadow began displaying. Seems that for the shadow to display there must be at least one visible border (or background) in it's view hierarchy. So be sure to have borderView.layer.borderWidth >= 0 with a non-transparent borderView.layer.borderColor (or a non-transparent background color)
-
Satyendra Pandey over 6 years@Suragch, I want to create rounded button with shadow effect so how can i add view in it ? If i am not wrong the click event will not be generated if view is added on button as subview ?
-
Ben Thomas about 6 yearsThe expression is "fool proof". :)
-
Ben Thomas about 6 yearsI was just correcting the English. :) The solution works.
-
inexcitus almost 6 yearsLike every other "solution" in this thread, it simply doesn't work, at least not on iOS 11.0 / Swift 4.1.
-
Baptiste Mille-Mathias over 5 yearsHi, thanks for your answer, you should add some comments to your code to explain a bit as described in How to Answer.
-
Thomás Pereira over 5 yearsDid you read "Swift 3" in the begin of the thread? So, it means that is a Swift 3 solution, I didn't test it in Swift 4.1 because I don't need it anymore. Feel free to edit the answer and give a solution. ;) Cheers
-
Sergey Grischyov over 5 years@Curnelious feel free to take a look at the updated answer with an Xcode project example. It cannot not work :)
-
Martin about 5 yearsIf you are using autolayout, a
layoutIfNeeded()
might be necessary before drawing the shadow path. -
D. Pratt almost 5 yearsThis is one of the best explanations I have come across on SO. Great job, and thank you so much!
-
Rishabh over 4 yearsThis worked for me too, just need to one thing more is to make all the subviews background-color to clear so that only container view has a visible background and this resolved my problem. Thanks!! @SergeyGrischyov
-
Andy Weinstein about 3 yearsThanks! This worked for me when I followed the example in GitHub closely. It requires a UIButton, and updating params and calling the addShadow function from viewDidLoad. I am wondering if it would be possible to generalize this into an IDesignable class based on UIView, which would not require any of the additional additionalization from outside (viewDidLoad).
-
Leon almost 3 yearsThis is a great solution which I have been using for a while, however it's worth recognising that this will stop your view's background colour updating for dar mode automatically. This can be fixed by manually setting the
layer.backgroundColor
intraitCollectionDidChange
. -
Xys over 2 yearsWon't work if you have both a borderWidth and a shadow
-
Xys over 2 yearsWrong solution, you shouldn't add layers inside your
layoutSubviews
method -
Xys over 2 yearsThis doesn't work since
borderWidth
requiresmasksToBounds
to betrue
but shadows to befalse