UIView with rounded corners and drop shadow?
Asked Answered
R

36

450

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 usual clipToBounds/maskToBounds conflicts occur.

enter image description here


Editor's note: Note that this extremely old question has been wholly outdated (for years) by changes in iOS.

Rotative answered 21/1, 2011 at 0:58 Comment(21)
Since you say in a comment below that you got this working using CoreGraphics, would you mind sharing the answer with the community so that you can help others in the same situation, as they tried to help you?Endive
I'm sorry, this was quite a long time ago, and I don't have the source anymore. What I did was override -drawRect: and use UIBezierPath to draw a rectangle, and apply a shadow to the layer backing the view... if I remember correctly. :)Rotative
The accepted answer does not work!Spry
Swift 3 & IBInspectable solution. ;) https://mcmap.net/q/23444/-uiview-with-rounded-corners-and-drop-shadowPortent
Possible duplicate of Giving UIView rounded cornersStutter
@Sachavijay You should verify the dates of both posts before you comment.Rotative
This is an way How can to do it. https://mcmap.net/q/23469/-can-39-t-add-a-corner-radius-and-a-shadowVasoinhibitor
Watch #1178275Doloritas
This would have to be the worst set of answers on the whole site. IT IS NOW INCREDIBLY EASY TO DO THIS, SEE ANSWER!!Gavel
@AdityaVaidyam can you please now uncheck the answer, it is totally wrong.Gavel
I've rolled this back to the last good question state. Answers, whether they are modern or not, should go in the answer section.Mcgrath
Hmm. The edit did not include an answer. I've added another edit that does not include an answer.Gavel
{ But by all means undo that edit, if seems more appropriate to do so ( @Mcgrath ). It's common on the site that question writers (who aren't SOBs) edit their questions with ongoing enormous changes to iOS (same with droid etc); I think it's pretty common to do it for disappearing users. (Just by the by, the amount of now utterly-useless garbage on SO is a huge problem Prosus will have to deal with sooner rather than later - I mean for the main topics on the site like droid, unity, ios, aws etc; questions about "real!" programming - computer science and such - don't suffer the problem, IMO)Gavel
@Fattie: it is IMO not a good edit, but I can leave it be to keep the peace. My reasoning is based on how contributors should conceive of question edits. Basically, when we change a question, we are (a) writing in the voice of the author, and (b) writing in the timeline of when the question was asked. This edit violates both principles. The author would not have written this anywhere in their question, because it is not reasonably part of a question. It is answer material.Mcgrath
Furthermore it appears that the question author wrote it; that authorial license is fine for spelling corrections and grammar improvements, but not for announcements about answers. It would be a minor improvement to add "Editor's note: " as a prefix, but it would be better to remove it entirely. The accepted answer is prominently marked, which is a fair place for it.Mcgrath
(Addendum: there is a third problem with the edit, which is that it is top posted. That is considered normal for email these days, but not forums, where it renders material in reverse chronological order. But this criticism is somewhat redundant, since switching to bottom-posting does not resolve the two other problems).Mcgrath
All makes sense. This is certainly a major issue for Prosus as (A) {again, as I mentioned this "elephant in the room" issue "only" applies to the overwhelmingly popular topics, ios, droid, unity etc, not "comp sci" questions ...} almost all answers are becoming, in a word, crap (B) you see this constantly in LLM training, the single biggest flat fail for GenAI code is precisely questions like this, where it gives the was-once-correct but is now totally wrong answer. I don't have a solution (obviously also I "don't care", as not a Prosus shareholder; I only care in the usual sense of ..Gavel
... the reason I want to "help there being great answers on this site" is so that I can get answers when I need it, and hence, pay for diapers etc.) That is a fantastic thought about "editor's note" you mentioned. Also,Gavel
in re the 2 points you mention, (1) I don't really see that, most edits are about removing "stylish" writing (people who are funny, people who mention crap like "I’ve been working on an application for a couple of years...", "hi how are you" etc), the entire point is to "normalize" the style to a calm academic-ish standard, and (2) well yes but that's the entire point of the issue; (a) the site can become completely useless or (b) both questioners and answerers must be forced to maintain their questions/answers {ticks must be moved once totally incorrect; answers that are long totallyGavel
incorrect must be deleted, etc) and/or (c) for disappearing users (which is excatly the usual cases with this issue) it has to be ok for others to do thsoe things. Note too that we're talking of REALLY EGREGIOUS cases. the answer here which is ticked (and was upvoted 460 times by presumably absolutely unknowledgeable bypassers!, setting aside the "legacy" issue) is just: absolute junk (it's literally a an example of what the question states famously doesn't work in ios!!! "the usual clipToBounds/maskToBounds conflicts occur..", lol.Gavel
Do not hesitate to edit from here as you see fit. For me, the "editors note" suggestion is brilliant, a game changer. (Note too that it's totally normal now for posters to be deceased; I for instance will surely be deceased in say 10 years and "10 years" is nothing on this site, I daily get DMs about answer of mine +10 yrs old. Someone is going to have to maintain my answers/questions when deceased.) Also - unrelated to this deep issue - surely the first sentence/paragraph of this question shoudl be just deleted? @McgrathGavel
F
464

Outdated

This answer is no longer applicable to supported versions of iOS.

See https://mcmap.net/q/23444/-uiview-with-rounded-corners-and-drop-shadow instead.


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.

Fanjet answered 21/1, 2011 at 1:4 Comment(23)
Well the problem with it is that when I set corner radius, it sets maskToBounds: YES, whereas the shadow requires clipToBounds: NO (where clipToBounds does the same as maskToBounds)Rotative
same problem here. If I have a background color I want that to be clipped to the rounded corners. To do that I have to use maskToBounds=TRUE, but then the shadow dissapears..Lorrielorrimer
For newbies like me: I had to import the QuartzCore framework into my project in order to call methods on the layer object.Mousebird
Note for people concerned with performance: This is not a fast operation. If you wanted to add a drop shadow to a button in every row of a table view for example, it would have the potential to cause some sluggish transitions and scrolling. Pre-rendering the shadow as an image and using that would be much more efficient.Josphinejoss
As several have pointed out, this doesn't work. Just set the view's background color to red and a corner radius greater than 2.f and you'll see.Wnw
It does work, but it has limitations. For example if you have another UIView near either of the corners, it will cover the rounding unless it's transparent. I just solved this in a very crude way by adding rounded corners to the inner view and putting a cover over its other edge to get the effect. Not ideal. Wish Apple would fix.Delorisdelorme
@Delorisdelorme That's why I said it doesn't work if you e.g. have a red background color for the view. It's a hack that works for some if not most use cases, but it's not the right answer technically.Wnw
Hi, this is working for me, if I'm creating a UIView through code(obviously masksToBounds is false). But, when I'm trying to use the above code on a UIView that is created from nib, only one works at a time. ie, shadow and corner radius doesn't work together. Please helpAfrica
The way to get this to work the right way is to use an inner container view, which will house your border and your background color, both with a corner radius. This view will be clipped to bounds! The second, outer container view will house the first one, has the same frame, with just a drop shadow. I've done this quite a few times to combine a border, drop shadow, and corner radius. It's really annoying, but it works really well.Wilton
Doesn't work. No idea why there are so many up votes. Was this applicable in older versions?Tullus
@EvanMulawski Im using 7.1. The maskToBounds when its off makes the rounded corner not applicable, and when its on, theres no shadow, as its outside the view.Tullus
@EvanMulawski, if you do maskToBounds as true, you will be stopping any kind of drop shadow to clip outside of the view's bounds. So I don't understand how you got a successful effect.Tullus
This doesn't work. The only way to do what the OP is asking is to use two views as @Wilton stated.Damara
Wrong Answer. border was drawn but corners not not mask.Cornia
Why does this work? it definitely worked for me, but i thought that if you add a shadow, the corners won't respect the cornerRadius.... Is it becuase of the quartzcore of someting?Verbal
Worked for me in Xamarin.iOS. Thanks a lot! I also made borderRadius = 200 so I have nice tint around my viewLevirate
for those wondering, you have to declare the corner radius FIRST then you can declare the shadow and they both work together, if you do it the other way around the corner radius code ends up clipping the shadow. It seems like the order would not matter, I too was surprised when it did indeed work when setting the corners first.Venetic
Hi again, my above comment seems to not explain the full picture. This works on view with just a backgroundColor for example, but on views with sub content (e.g. a UIImageView and its image) it won't clip the sub-content. So this will work with a UIView but not a UIImageView which explains why some comments/upvotes are in favor of it while others aren't.Venetic
why is it approved answer? The question contains clipsToBounds which won't workWhiteside
This answer is *utterly, totally, completely wrong, in literally every way. The fact that it hasn't been deleted is a hilarious fact of SO.Gavel
@Gavel It was not wrong 13 years ago, but I have added an Outdated heading and link to the better answer for current versions.Fanjet
Well done EvanW, but unfortunately it did not at all work then (or now). (The simple reason is if you don't clip the corners won't work - and if you do clip the shadow won't work.) I am not criticizing you, I am criticizing the SO system. (I put in wrong answers all the time, while still working something out etc - everyone does wrong answers. I personally just delete them, but, again my point is I am heavily criticizing the SO system for this horrible flaw.)Gavel
I hate to be churlish but this answer is now doubly wrong. The "outdated" implies it once worked and/or works but is old-fashioned. In fact it, extremely simply, never worked. Indeed this "answer" literally just uses the exact problem clearly stated in the question -!!! 'I can do each of those one by one but the usual clipToBounds/maskToBounds conflicts occur'. I'm sorry but this is literally not an answer. It's an "example of what doesn't work in iOS and literally an example of the problem at hand posed in the question".Gavel
D
748

Swift

enter image description here

// 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

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

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?

enter image description here

We can accomplish this with

blueView.layer.masksToBounds = true

(Alternatively, blueView.clipsToBounds = true gives the same result.)

enter image description here

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:

enter image description here

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.

Dovelike answered 25/1, 2016 at 2:7 Comment(10)
@Dovelike so for only border, we need to use separate view or separate views for corner radius and shadow too?Galantine
@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.Dovelike
This 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?Phlegethon
Not working, setting baseView.backgroundColor = UIColor.clear removes the shadow. Only if you set a background color will you see it.Gaily
@Markus, I retested the code in my answer and it still worked with Swift 4 and Xcode 9.Dovelike
@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.Dovelike
FYI 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)Casseycassi
@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 ?Deloisedelong
If you are using autolayout, a layoutIfNeeded() might be necessary before drawing the shadow path.Kish
This is one of the best explanations I have come across on SO. Great job, and thank you so much!Defeat
F
464

Outdated

This answer is no longer applicable to supported versions of iOS.

See https://mcmap.net/q/23444/-uiview-with-rounded-corners-and-drop-shadow instead.


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.

Fanjet answered 21/1, 2011 at 1:4 Comment(23)
Well the problem with it is that when I set corner radius, it sets maskToBounds: YES, whereas the shadow requires clipToBounds: NO (where clipToBounds does the same as maskToBounds)Rotative
same problem here. If I have a background color I want that to be clipped to the rounded corners. To do that I have to use maskToBounds=TRUE, but then the shadow dissapears..Lorrielorrimer
For newbies like me: I had to import the QuartzCore framework into my project in order to call methods on the layer object.Mousebird
Note for people concerned with performance: This is not a fast operation. If you wanted to add a drop shadow to a button in every row of a table view for example, it would have the potential to cause some sluggish transitions and scrolling. Pre-rendering the shadow as an image and using that would be much more efficient.Josphinejoss
As several have pointed out, this doesn't work. Just set the view's background color to red and a corner radius greater than 2.f and you'll see.Wnw
It does work, but it has limitations. For example if you have another UIView near either of the corners, it will cover the rounding unless it's transparent. I just solved this in a very crude way by adding rounded corners to the inner view and putting a cover over its other edge to get the effect. Not ideal. Wish Apple would fix.Delorisdelorme
@Delorisdelorme That's why I said it doesn't work if you e.g. have a red background color for the view. It's a hack that works for some if not most use cases, but it's not the right answer technically.Wnw
Hi, this is working for me, if I'm creating a UIView through code(obviously masksToBounds is false). But, when I'm trying to use the above code on a UIView that is created from nib, only one works at a time. ie, shadow and corner radius doesn't work together. Please helpAfrica
The way to get this to work the right way is to use an inner container view, which will house your border and your background color, both with a corner radius. This view will be clipped to bounds! The second, outer container view will house the first one, has the same frame, with just a drop shadow. I've done this quite a few times to combine a border, drop shadow, and corner radius. It's really annoying, but it works really well.Wilton
Doesn't work. No idea why there are so many up votes. Was this applicable in older versions?Tullus
@EvanMulawski Im using 7.1. The maskToBounds when its off makes the rounded corner not applicable, and when its on, theres no shadow, as its outside the view.Tullus
@EvanMulawski, if you do maskToBounds as true, you will be stopping any kind of drop shadow to clip outside of the view's bounds. So I don't understand how you got a successful effect.Tullus
This doesn't work. The only way to do what the OP is asking is to use two views as @Wilton stated.Damara
Wrong Answer. border was drawn but corners not not mask.Cornia
Why does this work? it definitely worked for me, but i thought that if you add a shadow, the corners won't respect the cornerRadius.... Is it becuase of the quartzcore of someting?Verbal
Worked for me in Xamarin.iOS. Thanks a lot! I also made borderRadius = 200 so I have nice tint around my viewLevirate
for those wondering, you have to declare the corner radius FIRST then you can declare the shadow and they both work together, if you do it the other way around the corner radius code ends up clipping the shadow. It seems like the order would not matter, I too was surprised when it did indeed work when setting the corners first.Venetic
Hi again, my above comment seems to not explain the full picture. This works on view with just a backgroundColor for example, but on views with sub content (e.g. a UIImageView and its image) it won't clip the sub-content. So this will work with a UIView but not a UIImageView which explains why some comments/upvotes are in favor of it while others aren't.Venetic
why is it approved answer? The question contains clipsToBounds which won't workWhiteside
This answer is *utterly, totally, completely wrong, in literally every way. The fact that it hasn't been deleted is a hilarious fact of SO.Gavel
@Gavel It was not wrong 13 years ago, but I have added an Outdated heading and link to the better answer for current versions.Fanjet
Well done EvanW, but unfortunately it did not at all work then (or now). (The simple reason is if you don't clip the corners won't work - and if you do clip the shadow won't work.) I am not criticizing you, I am criticizing the SO system. (I put in wrong answers all the time, while still working something out etc - everyone does wrong answers. I personally just delete them, but, again my point is I am heavily criticizing the SO system for this horrible flaw.)Gavel
I hate to be churlish but this answer is now doubly wrong. The "outdated" implies it once worked and/or works but is old-fashioned. In fact it, extremely simply, never worked. Indeed this "answer" literally just uses the exact problem clearly stated in the question -!!! 'I can do each of those one by one but the usual clipToBounds/maskToBounds conflicts occur'. I'm sorry but this is literally not an answer. It's an "example of what doesn't work in iOS and literally an example of the problem at hand posed in the question".Gavel
K
102

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:

result

Super easy and simple!

Kauri answered 8/4, 2017 at 15:10 Comment(7)
Does this work on buttons? Because it's not working on my end.Haukom
I 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.Hobby
Managed 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?Flofloat
@Curnelious feel free to take a look at the updated answer with an Xcode project example. It cannot not work :)Kauri
This 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!! @SergeyGrischyovRecrudescence
Thanks! 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).Fluecure
This 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 in traitCollectionDidChange.Elijaheliminate
L
79

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.

Loram answered 4/8, 2011 at 22:15 Comment(5)
Amit, you have to set maskToBounds/clipToBounds = YES for the * roundedView only *. DO NOT set this to the shadowView. I have not tried the above code but know for sure that this solution definitely works though not ideal. The higher shadowRadius takes care of the corner radius areas. Set the shadowRadius to 0 or 1 and you will notice what I am trying to say.Pate
Something like shadowView.layer.shadowOpacity = 0.6; is missingFakir
"shadowView.layer.opacity = 1.0" should be "shadowView.layer.shadowOpacity = 1.0"Seumas
Works on iOS 9 if use shadowView.layer.shadowOpacity = 1.0Consideration
Code fixed for shadowOpacityAfghanistan
D
43

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 ;
Dempsey answered 5/9, 2014 at 20:24 Comment(9)
Although all the other solutions work, and perhaps they are more general, this is by far the best solution to the problem. Adding subviews or sublayers create a world or pain trying to maintain the frame sizes, or, at best, may cause performance issues.Hamburg
This should be the answer. Clean and elegant.Dermis
Best solution and definitely elegant!Madness
Wow, this actually works. I don't understand why it should work -- you'd think the view's backgroundColor properly would directly map to the layer.backgroundColor property on iOS -- but it DOES WORK. (Xcode 8, Swift 3.) Well done, and thanks. This should be the accepted answer.Olva
I've created a Swift 3.1 version of your answer using UIView extension here - https://mcmap.net/q/23444/-uiview-with-rounded-corners-and-drop-shadow thanks for the inspiration!Kauri
You inspired me, I finally got It working! Thanks you. I did an extension to integrate IBInspectable your solution. https://mcmap.net/q/23444/-uiview-with-rounded-corners-and-drop-shadowPortent
The only way I found which works for me is to remove this line layer.shadowPath = [[UIBezierPath bezierPathWithRoundedRect:layer.bounds cornerRadius:layer.cornerRadius] CGPath];. Any idea why?Flofloat
if UIView is having subViews extreme at the margin, it fails.Ingenuity
Not sure why, but it is not working for me. Turning off clips to bounds prevents the corner radius from working in a filled UIButton. (i.e. not a border, a fill). The shadows work, but the corners do not.Bouffant
I
27

I solved the problem using the following trick when assigning shadow path for the container view :

[UIBezierPath bezierPathWithRoundedRect:cell.bounds cornerRadius:12]

Notice that the path given to the shadow is a rounded rectangle with the same corner radius as the background that the cell contains:

//this is the border for the UIView that is added to a cell
cell.backgroundView.layer.cornerRadius = 12;
cell.backgroundView.layer.masksToBounds = YES;
cell.backgroundView.layer.borderColor = [UIColor darkGrayColor].CGColor;
cell.backgroundView.layer.borderWidth = 1;

//this is the shadow around the cell itself (cannot have round corners with borders and shadow, need to use two views
cell.layer.shadowRadius = 2;
cell.layer.cornerRadius = 12;
cell.layer.masksToBounds = NO;
[[cell layer] setShadowColor:[[UIColor darkGrayColor] CGColor]];

[[cell layer] setShadowOffset:CGSizeMake(0.0,0.0)];
[[cell layer] setShadowOpacity:1.0];

UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:cell.bounds cornerRadius:12];
[[cell layer] setShadowPath:[path CGPath]];
Impregnate answered 24/3, 2013 at 2:54 Comment(1)
Best answer because it explains the right way to add shadow to a rounder cornered view. Thanks @Alex StoneSebaceous
A
17

If you are struggling because of the rounded corners vs. subviews vs. masksToBounds, then try using my function:

- (UIView*)putView:(UIView*)view insideShadowWithColor:(UIColor*)color andRadius:(CGFloat)shadowRadius andOffset:(CGSize)shadowOffset andOpacity:(CGFloat)shadowOpacity
{
    CGRect shadowFrame; // Modify this if needed
    shadowFrame.size.width = 0.f;
    shadowFrame.size.height = 0.f;
    shadowFrame.origin.x = 0.f;
    shadowFrame.origin.y = 0.f;
    UIView * shadow = [[UIView alloc] initWithFrame:shadowFrame];
    shadow.userInteractionEnabled = NO; // Modify this if needed
    shadow.layer.shadowColor = color.CGColor;
    shadow.layer.shadowOffset = shadowOffset;
    shadow.layer.shadowRadius = shadowRadius;
    shadow.layer.masksToBounds = NO;
    shadow.clipsToBounds = NO;
    shadow.layer.shadowOpacity = shadowOpacity;
    [view.superview insertSubview:shadow belowSubview:view];
    [shadow addSubview:view];
    return shadow;
}

call it on your view. whether your view has rounded corners, no matter its size, its shape - a nice shadow will be drawn.

Just keep the return value of the function so you can refer to it when you want to remove the table (or for example use insertSubview:aboveView:)

Avelar answered 19/10, 2012 at 7:51 Comment(8)
it works fine. But if view has gesture recognizers, then it will not work. how can we solve it?Millwork
@Millwork Do you see the lines where "// Modify this if needed" is specified? That's what you need. shadow.userInteractionEnabled = YES;Avelar
@Millwork then you should test the frames of the view and subview to see why. something probably is not right there. this exact code works for me in some very nice appsAvelar
This solution works great for UITableViews with rounded corners. Wish I could give it more up votes. Thanks!Implausible
@CarlosEduardoLópez Do you see the shadow.userInteractionEnabled = NO; // Modify this if needed line? So this is the case where needed. userInteractionEnabled is a basic and popular property you should be already familiar with :-)Avelar
I'm familiar with that, but that's not the problem. The problem is the CGRect position. If you change the background, you will notice the views are in the top corner, so you can enable the interaction, but they won't pass the gestures cause they're not in the same positionSocorrosocotra
Gestures are usually working fine with this, at least for me. You situation is probably different in an important mannerAvelar
Basically, I didn't pay attention to the comment in the line where CGRect shadowFrame is declared, that's why this solution didn't work with UIButton & UITableView. I just did shadowFrame = view.frame and erased those lines: shadowFrame.size.width = 0.f; shadowFrame.size.height = 0.f; shadowFrame.origin.x = 0.f; shadowFrame.origin.y = 0.f; Carlos Eduardo López solved and answered here: #25540377Virility
E
15

Using Swift 4 and Xcode 9, this is a working example of rounding an ImageView with a drop shadow, and a border.

    //set dimensions and position of image (in this case, centered)
    let imageHeight: CGFloat = 150, imageWidth: CGFloat = 150
    let xPosition = (self.view.frame.width / 2) - (imageWidth / 2)
    let yPosition = (self.view.frame.height / 2) - (imageHeight / 2)

    //set desired corner radius
    let cornerRadius: CGFloat = 20

    //create container for the image
    let imageContainer = UIView(frame: CGRect(x: xPosition, y: yPosition, width: imageWidth, height: imageHeight))

    //configure the container
    imageContainer.clipsToBounds = false
    imageContainer.layer.shadowColor = UIColor.black.cgColor
    imageContainer.layer.shadowOpacity = 1
    imageContainer.layer.shadowOffset = CGSize(width: 3.0, height: 3.0)
    imageContainer.layer.shadowRadius = 5
    imageContainer.layer.shadowPath = UIBezierPath(roundedRect: imageContainer.bounds, cornerRadius: cornerRadius).cgPath

    //create imageView
    let imageView = UIImageView(frame: imageContainer.bounds)

    //configure the imageView
    imageView.clipsToBounds = true
    imageView.layer.cornerRadius = cornerRadius
    //add a border (if required)
    imageView.layer.borderColor = UIColor.black.cgColor
    imageView.layer.borderWidth = 1.0
    //set the image
    imageView.image = UIImage(named: "bird")

    //add the views to the superview
    view.addSubview(imageContainer)
    imageContainer.addSubview(imageView)

enter image description here

If you want the image to be circular: (and shown without border)

let cornerRadius = imageWidth / 2

enter image description here

Emoryemote answered 1/5, 2018 at 20:46 Comment(0)
S
7

You need to use use shadowView and roundView

enter image description here

shadowView

  • Must has background color
  • Should lay behind roundView
  • The trick is to layout shadowView a bit inside, and its shadow needs to glow out. Adjust the insets so that shadowView is completely invisible behind roundView

roundView

  • Must clips subviews

The code

addSubviews(shadowView, roundView)
roundView.addSubviews(titleLabel, subtitleLabel, imageView)

// need inset
shadowView.pinEdges(view: self, inset: UIEdgeInsets(constraintInsets: 2))
roundView.pinEdges(view: self)

do {
  shadowView.backgroundColor = .white // need background
  let layer = shadowView.layer
  layer.shadowColor = UIColor.black.cgColor
  layer.shadowRadius = 3
  layer.shadowOffset = CGSize(width: 3, height: 3)
  layer.shadowOpacity = 0.7
  layer.shouldRasterize = true
}

do {
  roundView.backgroundColor = .white
  let layer = roundView.layer
  layer.masksToBounds = true
  layer.cornerRadius = 5
}

Or you can just do below without specifying clipToBounds/maskToBounds

layer.shadowColor = UIColor.gray.cgColor
layer.shadowOffset = CGSize(width: 3, height: 3)
layer.shadowOpacity = 0.8
Spry answered 24/4, 2017 at 12:53 Comment(0)
D
6

I've created a helper on UIView

@interface UIView (Helper)

- (void)roundCornerswithRadius:(float)cornerRadius
               andShadowOffset:(float)shadowOffset;
@end

you can call it like this

[self.view roundCornerswithRadius:5 andShadowOffset:5];

Here's the implementation

- (void)roundCornerswithRadius:(float)cornerRadius
               andShadowOffset:(float)shadowOffset
{
    const float CORNER_RADIUS = cornerRadius;
    const float SHADOW_OFFSET = shadowOffset;
    const float SHADOW_OPACITY = 0.5;
    const float SHADOW_RADIUS = 3.0;

    UIView *superView = self.superview;

    CGRect oldBackgroundFrame = self.frame;
    [self removeFromSuperview];

    CGRect frameForShadowView = CGRectMake(0, 0, oldBackgroundFrame.size.width, oldBackgroundFrame.size.height);
    UIView *shadowView = [[UIView alloc] initWithFrame:frameForShadowView];
    [shadowView.layer setShadowOpacity:SHADOW_OPACITY];
    [shadowView.layer setShadowRadius:SHADOW_RADIUS];
    [shadowView.layer setShadowOffset:CGSizeMake(SHADOW_OFFSET, SHADOW_OFFSET)];

    [self.layer setCornerRadius:CORNER_RADIUS];
    [self.layer setMasksToBounds:YES];

    [shadowView addSubview:self];
    [superView addSubview:shadowView];

}
Directed answered 12/6, 2013 at 14:26 Comment(2)
This is a good elegant solution. Ensure that your view has been added to its superview before use. I added some parameters to give me more control over the shadow, but overall works perfect. Thanks!Haehaecceity
This is nice solution but it doesn't work with autolayout: the view will be drawn on origin 0,0Industrious
F
6

Here is the solution that will work for sure!

I have created UIView extension with required edges to apply shadow on as below


enum AIEdge:Int {
    case
    Top,
    Left,
    Bottom,
    Right,
    Top_Left,
    Top_Right,
    Bottom_Left,
    Bottom_Right,
    All,
    None
}

extension UIView {
        
    func applyShadowWithCornerRadius(color:UIColor, opacity:Float, radius: CGFloat, edge:AIEdge, shadowSpace:CGFloat, cornerRadius: CGFloat)    {

        var sizeOffset:CGSize = CGSize.zero
        
        switch edge {
        case .Top:
            sizeOffset = CGSize(width: 0, height: -shadowSpace)
        case .Left:
            sizeOffset = CGSize(width: -shadowSpace, height: 0)
        case .Bottom:
            sizeOffset = CGSize(width: 0, height: shadowSpace)
        case .Right:
            sizeOffset = CGSize(width: shadowSpace, height: 0)
            
            
        case .Top_Left:
            sizeOffset = CGSize(width: -shadowSpace, height: -shadowSpace)
        case .Top_Right:
            sizeOffset = CGSize(width: shadowSpace, height: -shadowSpace)
        case .Bottom_Left:
            sizeOffset = CGSize(width: -shadowSpace, height: shadowSpace)
        case .Bottom_Right:
            sizeOffset = CGSize(width: shadowSpace, height: shadowSpace)
            
            
        case .All:
            sizeOffset = CGSize(width: 0, height: 0)
        case .None:
            sizeOffset = CGSize.zero
        }

        self.layer.cornerRadius = cornerRadius
        self.layer.masksToBounds = true

        self.layer.shadowColor = color.cgColor
        self.layer.shadowOpacity = opacity
        self.layer.shadowOffset = sizeOffset
        self.layer.shadowRadius = radius
        self.layer.masksToBounds = false

        self.layer.shadowPath = UIBezierPath(roundedRect:self.bounds, cornerRadius:self.layer.cornerRadius).cgPath
    }
}

Finally, you can call the shadow function as below for any of your UIView subclass, you can also specify the edge to apply shadow on, try different variations as per your need changing parameters of below method call.

viewRoundedToBeShadowedAsWell.applyShadowWithCornerRadius(color: .gray, opacity: 1, radius: 15, edge: AIEdge.All, shadowSpace: 15)

NOTE: If still that doesn't work, try calling it from MAIN THREAD

DispatchQueue.main.async {
   viewRoundedToBeShadowedAsWell.applyShadowWithCornerRadius(color: .gray, opacity: 1, radius: 15, edge: AIEdge.All, shadowSpace: 15)
}

Hope someone finds this useful !

Below is the result images:

enter image description here

enter image description here

enter image description here

Farthest answered 25/9, 2019 at 5:23 Comment(0)
F
5

After one whole day research of the round corner view with shadow, I'm glad to post my custom uiview class here, hope to end this question:

RoundCornerShadowView.h

#import <UIKit/UIKit.h>

@interface RoundCornerShadowView : UIView

@end

RoundCornerShadowView.m

#import "RoundCornerShadowView.h"

@implementation RoundCornerShadowView

// *** must override this method, not the other method ***
// otherwise, the background corner doesn't disappear....
// @2015/05/29
-(void) layoutSubviews {
    [super layoutSubviews];//is must to ensure rightly layout children view

    //1. first, create Inner layer with content
    CALayer *innerView = [CALayer layer];
    innerView.frame = CGRectMake(0,0,self.bounds.size.width,self.bounds.size.height);
    //instead of: innerView.frame = self.frame;
    innerView.borderWidth = 1.0f;
    innerView.cornerRadius = 6.0f;
    innerView.masksToBounds = YES;
    innerView.borderColor = [[UIColor lightGrayColor] CGColor];
    innerView.backgroundColor = [[UIColor whiteColor] CGColor];
    //put the layer to the BOTTOM of layers is also a MUST step...
    //otherwise this layer will overlay the sub uiviews in current uiview...
    [self.layer insertSublayer:innerView atIndex:0];

    //2. then, create shadow with self layer
    self.layer.masksToBounds = NO;
    self.layer.shadowColor = [[UIColor darkGrayColor] CGColor];
    self.layer.shadowOpacity = 0.4f;
    //shadow length
    self.layer.shadowRadius = 2.0f;
    //no offset
    self.layer.shadowOffset = CGSizeMake(0, 0);
    //right down shadow
    //[self.layer setShadowOffset: CGSizeMake(1.0f, 1.0f)];

    //3. last but important, MUST clear current view background color, or the color will show in the corner!
    self.backgroundColor = [UIColor clearColor];
}

@end

so, NO need to add subview in view or below in target view, just add one layer in current view, and do 3 step to complete it!

take a close look at to the comments in the code, it's helpful to understanding the component!

Farthingale answered 29/5, 2015 at 14:30 Comment(1)
Wrong solution, you shouldn't add layers inside your layoutSubviews methodAdvocaat
D
5

Swift 3 & IBInspectable solution:
Inspired by Ade's solution

First, create an UIView extension:

//
//  UIView-Extension.swift
//  

import Foundation
import UIKit

@IBDesignable
extension UIView {
     // Shadow
     @IBInspectable var shadow: Bool {
          get {
               return layer.shadowOpacity > 0.0
          }
          set {
               if newValue == true {
                    self.addShadow()
               }
          }
     }

     fileprivate func addShadow(shadowColor: CGColor = UIColor.black.cgColor, shadowOffset: CGSize = CGSize(width: 3.0, height: 3.0), shadowOpacity: Float = 0.35, shadowRadius: CGFloat = 5.0) {
          let layer = self.layer
          layer.masksToBounds = false

          layer.shadowColor = shadowColor
          layer.shadowOffset = shadowOffset
          layer.shadowRadius = shadowRadius
          layer.shadowOpacity = shadowOpacity
          layer.shadowPath = UIBezierPath(roundedRect: layer.bounds, cornerRadius: layer.cornerRadius).cgPath

          let backgroundColor = self.backgroundColor?.cgColor
          self.backgroundColor = nil
          layer.backgroundColor =  backgroundColor
     }


     // Corner radius
     @IBInspectable var circle: Bool {
          get {
               return layer.cornerRadius == self.bounds.width*0.5
          }
          set {
               if newValue == true {
                    self.cornerRadius = self.bounds.width*0.5
               }
          }
     }

     @IBInspectable var cornerRadius: CGFloat {
          get {
               return self.layer.cornerRadius
          }

          set {
               self.layer.cornerRadius = newValue
          }
     }


     // Borders
     // Border width
     @IBInspectable
     public var borderWidth: CGFloat {
          set {
               layer.borderWidth = newValue
          }

          get {
               return layer.borderWidth
          }
     }

     // Border color
     @IBInspectable
     public var borderColor: UIColor? {
          set {
               layer.borderColor = newValue?.cgColor
          }

          get {
               if let borderColor = layer.borderColor {
                    return UIColor(cgColor: borderColor)
               }
               return nil
          }
     }
}

Then, simply select your UIView in interface builder setting shadow ON and corner radius, like below:

Selecting your UIView

Setting shadow ON & corner radius

The result!

Result

Davin answered 13/5, 2017 at 22:18 Comment(3)
Like every other "solution" in this thread, it simply doesn't work, at least not on iOS 11.0 / Swift 4.1.Planchette
Did 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. ;) CheersPortent
Won't work if you have both a borderWidth and a shadowAdvocaat
B
5

Something swifty tested in swift 4

import UIKit

extension UIView {
    @IBInspectable var dropShadow: Bool {
        set{
            if newValue {
                layer.shadowColor = UIColor.black.cgColor
                layer.shadowOpacity = 0.4
                layer.shadowRadius = 1
                layer.shadowOffset = CGSize.zero
            } else {
                layer.shadowColor = UIColor.clear.cgColor
                layer.shadowOpacity = 0
                layer.shadowRadius = 0
                layer.shadowOffset = CGSize.zero
            }
        }
        get {
            return layer.shadowOpacity > 0
        }
    }
}

Produces

enter image description here

If you enable it in the Inspector like this:

enter image description here

It will add the User Defined Runtime Attribute, resulting in:

enter image description here

(I added previously the cornerRadius = 8)

:)

Butterworth answered 7/3, 2018 at 23:8 Comment(0)
L
4

iOS shadow and cornerRadius

[iOS CALayer]

You are able to setup shadow and/or cornerRadius using layer

//shadow
view1.layer.masksToBounds = false
view1.layer.shadowColor = UIColor.magenta.cgColor

view1.layer.shadowOffset = CGSize(width: 0, height: 0)
view1.layer.shadowOpacity = 1
view1.layer.shadowRadius = 5

//cornerRadius
view1.layer.masksToBounds = true
view1.layer.cornerRadius = 5

Visualization

1.shadowOffset.width, 2.shadowOffset.height, 3.shadowOpacity, 4. shadowRadius

  • shadowOffset width and height are any
  • shadowOpacity is from 0 to 1
  • shadowRadius is positive from 0

Shadow can go out of superview's frame when superview's clipsToBounds is false

cornerRadius plus shadow

  • When you use cornerRadius you set view.layer.masksToBounds = true to clip unnecesssary parts
  • When you use shadow you set view.layer.masksToBounds = false to display the shadow out of view

When you need both you can use different views for cornerRadius and shadow

Not simple task

Please note that shadow is not calculated only based on boundaries and cornerRaduis. During creating shadows next items are taken into account:

  • subviews layers
  • sublayers
  • CALayer.contents(Backing Image)
view1.backgroundColor = .clear
view1.layer.contents = UIImage(named: "ring")?.cgImage
view1.layer.contentsScale = UIScreen.main.scale

ScaleFactor(contentsScale, rasterizationScale) - by default is 1.0

currentBitmapSize = layerSize * scaleFactor

//non retina
1 point == 1x pixels

//Retina
1 point == 2x pixels
//or
1 point == 3x pixels

//For example to draw line 
point(width: 4, height: 2) == 1x pixels(width: 4, height: 2)(8 pixels) == 2x pixels(width: 8, height: 4)(32 pixels)

Use UIScreen.main.scale = scale factor of current screen

[iOS pixels vs points vs units]

Perfomance

Using CALayer.cornerRadius, shadow has some performance impact

As for CALayer.cornerRadius perfomance:

Applying it color blending[About] is used

As for shadow Xcode hint you to:

The layer is using dynamic shadows which are expensive to render. If possible try setting shadowPath, or pre-rendering the shadow into an image and putting it under the layer

1. Additionally use shadowPath

For internally static layer. By default it is nil that is why UIKit should create view off-screen and based on this information create a shadow. That is why you are able to predefine a path and set it. One more advantage is that you are able to create a custom shadow as you wish

view1.layer.shadowPath = UIBezierPath(roundedRect: view1.bounds, cornerRadius: 50).cgPath

Disadvantage - not dynamic. If view changes boundaries(width, height, cornerRadius...) the shadow remains as it was(old boundaries). If view's position is changed(moved, scrolled) shadowPath will be correct

2. Cache Rasterize

[iOS shouldRasterize]

Notes:

  • When view doesn't have a backgroundColor (clear/transparent) - shadow is applied for all subviews. Official doc

  • UITableView where every section has a shadow:

    • UITableView in Grouped or Inset Grouped Style
    • UITableView backgroundColor is clear
public func applyShadowForGroupedTableView(
  _ tableView: UITableView,
  shadowColor: CGColor,
  shadowOpacity: Float,
  shadowRadius: CGFloat,
  shadowOffset: CGSize
) {
  tableView.layer.masksToBounds = false
  tableView.layer.shadowColor = shadowColor
  tableView.layer.shadowOpacity = shadowOpacity
  tableView.layer.shadowRadius = shadowRadius
  tableView.layer.shadowOffset = shadowOffset
  tableView.layer.backgroundColor = UIColor.clear.cgColor
}
Litigious answered 10/9, 2022 at 12:22 Comment(1)
Got +1 for the detailed answer and the gif's! Great one!Dungeon
N
3

Here is the solution for masksToBounds conflict problem, it works for me.

After you set the corderRadius/borderColor/shadow and so on, set masksToBounds as NO:

v.layer.masksToBounds = NO;
Nisse answered 10/2, 2015 at 10:20 Comment(1)
This worked for me!! omg I almost did all the tricks above you answer! thanks Shaopeng.Dorton
D
3

Shadow + Border + Corner Radius enter image description here

    scrollview.backgroundColor = [UIColor whiteColor]; 
    CALayer *ScrlViewLayer = [scrollview layer];
    [ScrlViewLayer setMasksToBounds:NO ];
    [ScrlViewLayer setShadowColor:[[UIColor lightGrayColor] CGColor]];
    [ScrlViewLayer setShadowOpacity:1.0 ];
    [ScrlViewLayer setShadowRadius:6.0 ];
    [ScrlViewLayer setShadowOffset:CGSizeMake( 0 , 0 )];
    [ScrlViewLayer setShouldRasterize:YES];
    [ScrlViewLayer setCornerRadius:5.0];
    [ScrlViewLayer setBorderColor:[UIColor lightGrayColor].CGColor];
    [ScrlViewLayer setBorderWidth:1.0];
    [ScrlViewLayer setShadowPath:[UIBezierPath bezierPathWithRect:scrollview.bounds].CGPath];
Devinne answered 20/5, 2015 at 5:36 Comment(0)
R
3

Here is my version in Swift 3 for a UIView

let corners:UIRectCorner = [.bottomLeft, .topRight]
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()

mask.path = path.cgPath
mask.fillColor = UIColor.white.cgColor

let shadowLayer = CAShapeLayer()
shadowLayer.shadowColor = UIColor.black.cgColor
shadowLayer.shadowOffset = CGSize(width: 0.0, height: 4.0)
shadowLayer.shadowRadius = 6.0
shadowLayer.shadowOpacity = 0.25
shadowLayer.shadowPath = mask.path

self.layer.insertSublayer(shadowLayer, at: 0)
self.layer.insertSublayer(mask, at: 1)
Ratchford answered 22/12, 2016 at 17:14 Comment(0)
D
3

Swift 4 : Create Subclass of UIView

class ShadowView: UIView {

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        // corner radius
        self.layer.cornerRadius = 10

        // border
        self.layer.borderWidth = 1.0
        self.layer.borderColor = UIColor.black.cgColor

        // shadow
        self.layer.shadowColor = UIColor.black.cgColor
        self.layer.shadowOffset = CGSize(width: 3, height: 3)
        self.layer.shadowOpacity = 0.7
        self.layer.shadowRadius = 4.0
    }

}

Using..

Use Class Shadow View

Delaine answered 1/12, 2017 at 10:52 Comment(1)
This doesn't work since borderWidth requires masksToBounds to be true but shadows to be falseAdvocaat
A
2

Well if you don't want to change your nibs and view hierarchy as suggested David C. this method will do it for you. To add rounded corners and shadow to your UIImageView just use this method, for example:

[Utils roundCornersForImageView:myImageView withCornerRadius:6.0 
andShadowOffset:2.0];

(!) For performance reasons I don't think it is a good idea to use this code in something like UITableView, since this code changes view hierarchy. So I will suggest to change your nib and add a container view for shadow effect and use Davic C. code.

+ (void)roundCornersForImageView:(UIImageView *)imageView 
withCornerRadius:(float)cornerRadius andShadowOffset:(float)shadowOffset
{
    const float CORNER_RADIUS = cornerRadius;
    const float BORDER_WIDTH = 1.0; 
    const float SHADOW_OFFSET = shadowOffset;
    const float SHADOW_OPACITY = 0.8;
    const float SHADOW_RADIUS = 3.0;

    //Our old image now is just background image view with shadow
    UIImageView *backgroundImageView = imageView;
    UIView *superView = backgroundImageView.superview;

    //Make wider actual visible rect taking into account shadow
    //offset
    CGRect oldBackgroundFrame = backgroundImageView.frame;
    CGRect newBackgroundFrame = CGRectMake(oldBackgroundFrame.origin.x, oldBackgroundFrame.origin.y, oldBackgroundFrame.size.width + SHADOW_OFFSET, oldBackgroundFrame.size.height + SHADOW_OFFSET);
    [backgroundImageView removeFromSuperview];
    backgroundImageView.frame = newBackgroundFrame;        

    //Make new UIImageView with rounded corners and put our old image
    CGRect frameForRoundedImageView = CGRectMake(0, 0, oldBackgroundFrame.size.width, oldBackgroundFrame.size.height);
    UIImageView *roundedImageView = [[UIImageView alloc]initWithFrame:frameForRoundedImageView];
    roundedImageView.image = imageView.image;
    [roundedImageView.layer setCornerRadius:CORNER_RADIUS];
    [roundedImageView.layer setBorderColor:[UIColor lightGrayColor].CGColor];        
    [roundedImageView.layer setBorderWidth:BORDER_WIDTH]; 
    [roundedImageView.layer setMasksToBounds:YES];

    //Set shadow preferences
    [backgroundImageView setImage:nil];
    [backgroundImageView.layer setShadowColor:[UIColor blackColor].CGColor];
    [backgroundImageView.layer setShadowOpacity:SHADOW_OPACITY];
    [backgroundImageView.layer setShadowRadius:SHADOW_RADIUS];
    [backgroundImageView.layer setShadowOffset:CGSizeMake(SHADOW_OFFSET, SHADOW_OFFSET)];   

    //Add out two image views back to the view hierarchy.
    [backgroundImageView addSubview:roundedImageView];
    [superView addSubview:backgroundImageView];   
}    
Astral answered 20/7, 2012 at 11:2 Comment(0)
H
2

Old thread still current...

I've edited Daniel Gindi's method to make it possible to use it with buttons etc. as well. If anyone needs rounded corners or wants to combine round corners and a border it has to be set on the view's layer which is passed to this method. I've also set the rasterization to speed it up a little.

+ (UIView*)putView:(UIView*)view insideShadowWithColor:(CGColorRef)color 
                                 andRadius:(CGFloat)shadowRadius 
                                 andOffset:(CGSize)shadowOffset 
                                 andOpacity:(CGFloat)shadowOpacity
{
    // Must have same position like "view"
    UIView *shadow = [[UIView alloc] initWithFrame:view.frame]; 

    shadow.layer.contentsScale = [UIScreen mainScreen].scale;
    shadow.userInteractionEnabled = YES; // Modify this if needed
    shadow.layer.shadowColor = color;
    shadow.layer.shadowOffset = shadowOffset;
    shadow.layer.shadowRadius = shadowRadius;
    shadow.layer.masksToBounds = NO;
    shadow.clipsToBounds = NO;
    shadow.layer.shadowOpacity = shadowOpacity;
    shadow.layer.rasterizationScale = [UIScreen mainScreen].scale;
    shadow.layer.shouldRasterize = YES;

    [view.superview insertSubview:shadow belowSubview:view];
    [shadow addSubview:view];

    // Move view to the top left corner inside the shadowview 
    // ---> Buttons etc are working again :)
    view.frame = CGRectMake(0, 0, view.frame.size.width, view.frame.size.height);

    return shadow;
}
Hanuman answered 12/10, 2013 at 12:39 Comment(0)
S
2

The following worked best for me (this code lies in UIView extension, so self denotes some UIView to which we must add a shadow and round corner)

- (void)addShadowViewWithCornerRadius:(CGFloat)radius {

UIView *container = self.superview;

if (!container) {
    return;
}

UIView *shadowView = [[UIView alloc] init];
shadowView.translatesAutoresizingMaskIntoConstraints = NO;
shadowView.backgroundColor = [UIColor lightGrayColor];
shadowView.layer.cornerRadius = radius;
shadowView.layer.masksToBounds = YES;

[container addSubview:shadowView];
[container bringSubviewToFront:shadowView];

[container addConstraint:[NSLayoutConstraint constraintWithItem:shadowView
                                                      attribute:NSLayoutAttributeWidth
                                                      relatedBy:NSLayoutRelationEqual
                                                         toItem:self
                                                      attribute:NSLayoutAttributeWidth
                                                     multiplier:1.0
                                                       constant:0.0]];
[container addConstraint:[NSLayoutConstraint constraintWithItem:shadowView
                                                      attribute:NSLayoutAttributeLeading
                                                      relatedBy:NSLayoutRelationEqual
                                                         toItem:self
                                                      attribute:NSLayoutAttributeLeading
                                                     multiplier:1.0
                                                       constant:2.0]];

[container addConstraint:[NSLayoutConstraint constraintWithItem:shadowView
                                                      attribute:NSLayoutAttributeHeight
                                                      relatedBy:NSLayoutRelationEqual
                                                         toItem:self
                                                      attribute:NSLayoutAttributeHeight
                                                     multiplier:1.0
                                                       constant:0.0]];
[container addConstraint:[NSLayoutConstraint constraintWithItem:shadowView
                                                      attribute:NSLayoutAttributeTop
                                                      relatedBy:NSLayoutRelationEqual
                                                         toItem:self
                                                      attribute:NSLayoutAttributeTop
                                                     multiplier:1.0
                                                       constant:2.0]];
[container sendSubviewToBack:shadowView];
}

The main difference between this and other code samples is that this adds the shadow view as a sibling view (as against adding the current view as subview of shadow view), thereby eliminating the need to modify the existing view hierarchy in any way.

Staminody answered 1/2, 2016 at 4:40 Comment(0)
S
2

Swift 4 Solution for making UICollectionViewCell round and adding Shadows, without any extensions and complications :)

Note: For simple views e.g Buttons. See the @suragch's Answer in this post. https://mcmap.net/q/23444/-uiview-with-rounded-corners-and-drop-shadow. Tested successfully for buttons

In case if any one still struggling to round the corners and add shadows at the same time. Although this solution works with UICollectionViewCell, it can be generalized to any view.

This technique worked for me without making any extensions and all the complicated stuff. I am working with storyBoard.

Technique

You must add a UIView (lets say it "containerView") inside your UICollectionViewCell in storyBoard and add all the required views (buttons, images etc) inside this containerView. See the Screenshot. Structure of Cell

Connect the outlet for containerView. Add following lines of code in CellforItemAtIndexPath delegate function.

//adds shadow to the layer of cell

cell.layer.cornerRadius = 3.0
    cell.layer.masksToBounds = false
    cell.layer.shadowColor = UIColor.black.cgColor
    cell.layer.shadowOffset = CGSize(width: 0, height: 0)
    cell.layer.shadowOpacity = 0.6

//makes the cell round 

let containerView = cell.containerView!
    containerView.layer.cornerRadius = 8
    containerView.clipsToBounds = true

Output

See the simulator Screenshot Rounded corners with Shadows (UICollectionViewCell)

Sessions answered 30/1, 2018 at 12:10 Comment(0)
B
1

daniel.gindi's answer above did the trick for me! (+1 daniel) However, I had to make minor adjustments - change the shadowFrame size to be same as view's frame size, and enable user interaction. Here's the updated code:

+ (UIView*)putView:(UIView*)view insideShadowWithColor:(UIColor*)color andRadius:(CGFloat)shadowRadius andOffset:(CGSize)shadowOffset andOpacity:(CGFloat)shadowOpacity
{
    CGRect shadowFrame; // Modify this if needed

    // Modified this line
    shadowFrame.size = CGSizeMake(view.frame.size.width, view.frame.size.height);

    shadowFrame.origin.x = 0.f;
    shadowFrame.origin.y = 0.f;
    UIView * shadow = [[UIView alloc] initWithFrame:shadowFrame];

    // Modified this line
    shadow.userInteractionEnabled = YES;
    shadow.layer.shadowColor = color.CGColor;
    shadow.layer.shadowOffset = shadowOffset;
    shadow.layer.shadowRadius = shadowRadius;
    shadow.layer.masksToBounds = NO;
    shadow.clipsToBounds = NO;
    shadow.layer.shadowOpacity = shadowOpacity;

    [shadow addSubview:view];
    return shadow;
}

I would like to add that in my case, I was trying to add this to a 3rd party view controller, i.e. I did not have direct control over the code. So, here's how I used the function above:

UIView *shadow = [self putView:vc.view 
         insideShadowWithColor:[UIColor blackColor]
                     andRadius:5.0 
                     andOffset:CGSizeMake(0.0, 0.0) 
                    andOpacity:1.0];
vc.view = shadow;
vc.view.layer.cornerRadius = 5.0;
vc.view.layer.masksToBounds = YES;
Buskus answered 29/4, 2014 at 13:6 Comment(0)
M
1

I make some changes to the code of daniel.gindi

This is all you need to make it work.

+ (void)putView:(UIView*)view insideShadowWithColor:(UIColor*)color andBlur:         (CGFloat)blur andOffset:(CGSize)shadowOffset andOpacity:(CGFloat)shadowOpacity
{
    CGRect shadowFrame = view.frame;
    UIView * shadow = [[UIView alloc] initWithFrame:shadowFrame];
    shadow.backgroundColor = [UIColor redColor];
    shadow.userInteractionEnabled = YES; // Modify this if needed
    shadow.layer.shadowColor = color.CGColor;
    shadow.layer.shadowOffset = shadowOffset;
    shadow.layer.shadowRadius = blur;
    shadow.layer.cornerRadius = view.layer.cornerRadius;
    shadow.layer.masksToBounds = NO;
    shadow.clipsToBounds = NO;
    shadow.layer.shadowOpacity = shadowOpacity;
    [view.superview insertSubview:shadow belowSubview:view];
}
Mealworm answered 28/8, 2014 at 15:0 Comment(0)
M
1

You need to use two UIViews to achieve this. One UIView will work like shadow and other one will work for rounded border.

Here is a code snippet a Class Method with a help of a protocol:

@implementation UIMethods

+ (UIView *)genComposeButton:(UIViewController <UIComposeButtonDelegate> *)observer;
{
    UIView *shadow = [[UIView alloc]init];
    shadow.layer.cornerRadius = 5.0;
    shadow.layer.shadowColor = [[UIColor blackColor] CGColor];
    shadow.layer.shadowOpacity = 1.0;
    shadow.layer.shadowRadius = 10.0;
    shadow.layer.shadowOffset = CGSizeMake(0.0f, -0.5f);

    UIButton *btnCompose = [[UIButton alloc]initWithFrame:CGRectMake(0, 0,60, 60)];
    [btnCompose setUserInteractionEnabled:YES];
    btnCompose.layer.cornerRadius = 30;
    btnCompose.layer.masksToBounds = YES;
    [btnCompose setImage:[UIImage imageNamed:@"60x60"] forState:UIControlStateNormal];
    [btnCompose addTarget:observer action:@selector(btnCompose_click:) forControlEvents:UIControlEventTouchUpInside];
    [shadow addSubview:btnCompose];
    return shadow;
}

In the code above btnCompose_click: will become a @required delegate method which will fire on the button click.

And here I added a button to my UIViewController like this:

UIView *btnCompose = [UIMethods genComposeButton:self];
btnCompose.frame = CGRectMake(self.view.frame.size.width - 75,
                          self.view.frame.size.height - 75,
                          60, 60);
[self.view addSubview:btnCompose];

The result will look like this:

enter image description here

Monolithic answered 8/4, 2015 at 6:29 Comment(0)
K
1

I have tried so many solutions from this post and ended up with the below solution. This is full proof solution unless you need to drop shadow on a clear color view.

- (void)addShadowWithRadius:(CGFloat)shadowRadius withOpacity:(CGFloat)shadowOpacity withOffset:(CGSize)shadowOffset withColor:(UIColor *)shadowColor withCornerradius:(CGFloat)cornerRadius
{
    UIView *viewShadow = [[UIView alloc]initWithFrame:self.frame];
    viewShadow.backgroundColor = [UIColor whiteColor];
    viewShadow.layer.shadowColor = shadowColor.CGColor;
    viewShadow.layer.shadowOffset = shadowOffset;
    viewShadow.layer.shadowRadius = shadowRadius;
    viewShadow.layer.shadowOpacity = shadowOpacity;
    viewShadow.layer.cornerRadius = cornerRadius;
    viewShadow.layer.masksToBounds = NO;
    [self.superview insertSubview:viewShadow belowSubview:self];

    [viewShadow setTranslatesAutoresizingMaskIntoConstraints:NO];
    [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:viewShadow attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]];
    [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:viewShadow attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0]];
    [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:viewShadow attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:viewShadow attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
    [self.superview addConstraint:[NSLayoutConstraint constraintWithItem:viewShadow attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:viewShadow attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]];
    [self layoutIfNeeded];

    self.layer.cornerRadius = cornerRadius;
    self.layer.masksToBounds = YES;
}
Kuching answered 29/12, 2016 at 13:22 Comment(2)
The expression is "fool proof". :)Unpracticed
I was just correcting the English. :) The solution works.Unpracticed
I
1
import UIKit

extension UIView {

    func addShadow(shadowColor: UIColor, offSet: CGSize, opacity: Float, shadowRadius: CGFloat, cornerRadius: CGFloat, corners: UIRectCorner, fillColor: UIColor = .white) {

        let shadowLayer = CAShapeLayer()
        let size = CGSize(width: cornerRadius, height: cornerRadius)
        let cgPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: size).cgPath //1
        shadowLayer.path = cgPath //2
        shadowLayer.fillColor = fillColor.cgColor //3
        shadowLayer.shadowColor = shadowColor.cgColor //4
        shadowLayer.shadowPath = cgPath
        shadowLayer.shadowOffset = offSet //5
        shadowLayer.shadowOpacity = opacity
        shadowLayer.shadowRadius = shadowRadius
        self.layer.addSublayer(shadowLayer)
    }
}
Ipswich answered 19/9, 2019 at 4:45 Comment(0)
D
0

The answer provided by Evan Mulawski will work perfectly. The catch is that you have to set the background color for the view to clearColor and the masksToBounds property to NO.

You can set whatever color you want for the view, set it like

v.layer.backgroundColor = your color;

Hope this helps..

Demon answered 22/11, 2013 at 11:41 Comment(0)
V
0

This is how you do it, with rounded corners and rounded shadows without bothering with paths.

//Inner view with content
[imageView.layer setBorderColor:[[UIColor lightGrayColor] CGColor]];
[imageView.layer setBorderWidth:1.0f];
[imageView.layer setCornerRadius:8.0f];
[imageView.layer setMasksToBounds:YES];

//Outer view with shadow
UIView* shadowContainer = [[UIView alloc] initWithFrame:imageView.frame];
[shadowContainer.layer setMasksToBounds:NO];
[shadowContainer.layer setShadowColor:[[UIColor blackColor] CGColor]];
[shadowContainer.layer setShadowOpacity:0.6f];
[shadowContainer.layer setShadowRadius:2.0f];
[shadowContainer.layer setShadowOffset: CGSizeMake(0.0f, 2.0f)];

[shadowContainer addSubview:imageView];

The view with content, in my case a UIImageView, has a corner radius and therefore has to mask to bounds.

We create another equally sized view for the shadows, set it's maskToBounds to NO and then add the content view to the container view (e.g. shadowContainer).

Vaughan answered 23/4, 2015 at 5:20 Comment(0)
G
0

I write this UIView category method to solve this problem, uses separate views for the shadow and the corner radius.

-(UIView *)shadowedWrapViewWithBounds:(CGRect)bounds {
UIView *baseView = [[UIView alloc] init];
baseView.bounds = bounds;
baseView.backgroundColor = [UIColor clearColor];
baseView.layer.shadowColor = [UIColor blackColor].CGColor;
baseView.layer.shadowOffset = CGSizeMake(0, 0);
baseView.layer.shadowOpacity = 0.7;
baseView.layer.shadowRadius = 4.0;

// improve performance
baseView.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:baseView.bounds cornerRadius:4].CGPath;
baseView.layer.shouldRasterize = YES;
baseView.layer.rasterizationScale = [UIScreen mainScreen].scale;

[baseView addSubview:self];
//use Masonry autolayout, self can set corner radius
[self makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(baseView);
}];

return baseView;
}
Glossectomy answered 11/4, 2017 at 9:17 Comment(0)
E
0
extension UIView {
    func dropRoundedShadowForAllSides() {
        let backgroundView = UIView(frame:self.frame)
        let radius = frame.height/2
        backgroundView.layer.masksToBounds = false
        self.layer.masksToBounds = true
        backgroundView.layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
        backgroundView.layer.shadowRadius = 4
        backgroundView.layer.shadowOpacity = 0.4

        let path = UIBezierPath()

        // Start at the Top Left Corner + radius distance
        path.move(to: CGPoint(x: 2*radius, y: 0.0))

        // Move to the Top Right Corner - radius distance
        path.addLine(to: CGPoint(x: backgroundView.frame.size.width - radius, y: 0.0))

        // Move to top right corner + radius down as curve
        let centerPoint1 = CGPoint(x:backgroundView.frame.size.width - radius,y:radius)
        path.addArc(withCenter: centerPoint1, radius: radius, startAngle: 3*(.pi/2), endAngle: 0, clockwise: true)

        // Move to the Bottom Right Corner - radius
        path.addLine(to: CGPoint(x: backgroundView.frame.size.width, y: backgroundView.frame.size.height - radius))

        // Move to top right corner + radius left as curve
        let centerPoint2 = CGPoint(x:backgroundView.frame.size.width - radius,y:backgroundView.frame.size.height - radius)
        path.addArc(withCenter: centerPoint2, radius: radius, startAngle: 0, endAngle: .pi/2, clockwise: true)

        // Move to the Bottom Left Corner - radius
        path.addLine(to: CGPoint(x: radius, y: backgroundView.frame.size.height))

        // Move to left right corner - radius up as curve
        let centerPoint3 = CGPoint(x:radius,y:backgroundView.frame.size.height - radius)
        path.addArc(withCenter: centerPoint3, radius: radius, startAngle: .pi/2, endAngle: .pi, clockwise: true)

        // Move to the top Left Corner - radius
        path.addLine(to: CGPoint(x: 0, y: radius))

        // Move to top right corner + radius down as curve
        let centerPoint4 = CGPoint(x:radius,y:radius)
        path.addArc(withCenter: centerPoint4, radius: radius, startAngle: .pi, endAngle: 3 * (.pi/2), clockwise: true)

        path.close()

        backgroundView.layer.shadowPath = path.cgPath
        if let superView = self.superview {
            superView.addSubview(backgroundView)
            superView.sendSubview(toBack: backgroundView)
            superView.bringSubview(toFront: self)
        }

    }
}
Epirogeny answered 30/8, 2018 at 15:1 Comment(1)
Hi, thanks for your answer, you should add some comments to your code to explain a bit as described in How to Answer.Cuddle
N
0

If you specifically want custom rounded corners for UIButtons, there are many different ways of achieving that.

The following code examples (thanks to Erica) give a nice overview over all the possibilities.

Rounded corners button before iOS 15

Before iOS 15, you can make a rounded corners button by setting layer.cornerRadius, backgroundColor, and setTitleColor.

let button = UIButton(type: .system)
button.setTitle("Button", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .systemPink
button.layer.cornerRadius = 8

button.contentEdgeInsets = UIEdgeInsets(
  top: 10,
  left: 20,
  bottom: 10,
  right: 20
)

Capsule button

If we increase the corner radius with a large enough value, you can create a capsule-style button. To create a capsule style button, we set corner radius equals the half of a button height. Since a button height might vary based on title size or layout, I usually create a UIButton subclass for a capsule style.

class CapsuleButton: UIButton {
  override func layoutSubviews() {
    super.layoutSubviews()
    let height = bounds.height
    layer.cornerRadius = height/2
  }
}

let capsule = CapsuleButton(type: .system)
capsule.setTitle("Button", for: .normal)
capsule.setTitleColor(.white, for: .normal)
capsule.backgroundColor = .systemPink
capsule.contentEdgeInsets = UIEdgeInsets(
  top: 10,
  left: 20,
  bottom: 10,
  right: 20
)

Smooth corners

If you want to replicate Apple smooth corners, you can also do that with the cornerCurve property. To create a continuous smooth corner, you set layer.cornerCurve of a button to .continuous.

let button = UIButton(type: .system)
button.setTitle("Button", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .systemPink
button.layer.cornerRadius = 8
button.layer.cornerCurve = .continuous

button.contentEdgeInsets = UIEdgeInsets(
  top: 10,
  left: 20,
  bottom: 10,
  right: 20
)

Rounded corners button in iOS 15 using UIButton.Configuration

In iOS 15, Apple introduces a new way to customize a button via UIButton.Configuration.

var configuration = UIButton.Configuration.filled()

configuration.title = "Button"
configuration.baseBackgroundColor = UIColor.systemPink
configuration.contentInsets = NSDirectionalEdgeInsets(
  top: 10,
  leading: 20,
  bottom: 10,
  trailing: 20
)

let button = UIButton(configuration: configuration, primaryAction: nil)

Control corner radius using cornerStyle

UIButton.Configuration has many built-in corner styles for you to choose from. You can set this with the cornerStyle property.

configuration.cornerStyle = .small
configuration.cornerStyle = .medium
configuration.cornerStyle = .large
configuration.cornerStyle = .capsule

Custom corner radius

If you want something more unique, you can specify the corner radius via cornerRadius of UIBackgroundConfiguration.

var configuration = UIButton.Configuration.filled()
configuration.title = "Button"
configuration.baseBackgroundColor = UIColor.systemPink
configuration.contentInsets = NSDirectionalEdgeInsets(
  top: 10,
  leading: 20,
  bottom: 10,
  trailing: 20
)
configuration.background.cornerRadius = 20

configuration.cornerStyle = .fixed


let button = UIButton(configuration: configuration, primaryAction: nil)
Nonessential answered 3/11, 2022 at 11:23 Comment(0)
G
0

2024 NOW DEAD-EASY, .maskedCorners:

Per user @HangarRash, has been available since iOS11:

class LiveNews: UIIView {
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        // Do this:
        clipsToBounds = false
        
        // The corners:
        backgroundColor = .systemBlue
        layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner,
                                 .layerMaxXMaxYCorner, .layerMaxXMinYCorner]
        layer.cornerRadius = 20
        
        // The shadow:
        layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: 20).cgPath
        layer.shadowColor = UIColor.systemYellow.cgColor
        layer.shadowRadius = 1
        layer.shadowOpacity = 1
        layer.shadowOffset = CGSize(width: -4, height: 4)
    }
}

IT IS THAT SIMPLE:

You can set the corners, and the shadow, totally independently. Done.

enter image description here

enter image description here

enter image description here

Critical "slightly expand the shadow" tip:

You almost always want:

.shadowPath = UIBezierPath(roundedRect: bounds.insetBy(dx: -4, dy: -4), etc

to expand the size of the shadow a little.

Footnote:

Obviously, as an iOS engineering matter, you need put only the .shadowPath actually in layoutSubviews. Most teams put the other initialization of a view in some "common init" call. To save you typing ...

class SweetTopRoundedPanel: UIIView {
    
    override func yourInitCall() {
        super.yourInitCall()
        
        clipsToBounds = false
        
        backgroundColor = .secondarySystemBackground
        layer.cornerCurve = .continuous
        layer.cornerRadius = 40
        layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
        
        layer.borderColor = UIColor.systemPink.cgColor
        layer.borderWidth = 1
        
        layer.shadowColor = UIColor.systemYellow.cgColor
        layer.shadowRadius = 5
        layer.shadowOpacity = 0.7
        layer.shadowOffset = CGSize(width: 0, height: 0)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        layer.shadowPath = UIBezierPath(
            roundedRect: bounds.insetBy(dx: -3, dy: -2),
            cornerRadius: layer.cornerRadius).cgPath
    }
}

Footnote to footnote:

As night follows day, a couple folks DM'd asking how to do the Most teams put the other initialization of a view in some "common init" call part. Others may have something better but for UIView I do this

// file UIIView.swift (note the extra "I" !)

import UIKit

///Note the extra "I". Adds "I"initialization to the UIKit class.
class UIIView: UIView {

    override init(frame: CGRect) {
        
        super.init(frame: frame)
        common()
    }

    required init?(coder aDecoder: NSCoder) {
        
        super.init(coder: aDecoder)
        common()
    }

    func common() {

    }
}
Gavel answered 23/2, 2024 at 15:22 Comment(2)
maskedCorners was added in iOS 11. Your code should have been valid since 2017.Gasolier
great info! (Note though, I was just pointing out that it's dead easy in 2024, and making no further claims!)Gavel
W
-1

You need add masksToBounds = true for combined between corderRadius shadowRadius.

button.layer.masksToBounds = false;
Weasner answered 21/9, 2017 at 13:27 Comment(0)
K
-3
var shadows = UIView()
shadows.frame = view.frame
shadows.clipsToBounds = false
view.addSubview(shadows)


let shadowPath0 = UIBezierPath(roundedRect: shadows.bounds, cornerRadius: 10)
let layer0 = CALayer()
layer0.shadowPath = shadowPath0.cgPath
layer0.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.23).cgColor
layer0.shadowOpacity = 1
layer0.shadowRadius = 6
layer0.shadowOffset = CGSize(width: 0, height: 3)
layer0.bounds = shadows.bounds
layer0.position = shadows.center

shadows.layer.addSublayer(layer0)

Kelbee answered 7/4, 2020 at 7:3 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.