Can't add a corner radius and a shadow Can't add a corner radius and a shadow objective-c objective-c

Can't add a corner radius and a shadow


Yes, yes there is...

If you want both a corner radius and a drop shadow, you don't turn on -masksToBounds, but rather set the corner radius and set the bezier path of the shadow with a rounded rect. Keep the radius of the two the same:

[layer setShadowOffset:CGSizeMake(0, 3)];[layer setShadowOpacity:0.4];[layer setShadowRadius:3.0f];[layer setShouldRasterize:YES];[layer setCornerRadius:12.0f];[layer setShadowPath:                   [[UIBezierPath bezierPathWithRoundedRect:[self bounds]                                               cornerRadius:12.0f] CGPath]];

You might want to check your performance without the -shouldRasterize parameter set once you're setting the shadow path. Drawing performance tends to be very good once you've set a shadow path.

UPDATE

I hadn't looked at this problem in quite awhile, but it appears that you no longer need to set a shadowPath in order to get this to work. Simply setting the cornerRadius and shadowOpacity will work now. I think this has been the case since iOS5 (as far as I can tell). Providing this update is probably unnecessary since setting those parameters 'just works', but I'll provide it for posterity sake. To recap, this is now all you need:

[layer setShadowOpacity:0.4];[layer setCornerRadius:12.0f];

If you still need better performance, you can go ahead and set the shouldRasterize parameter as well:

[layer setShouldRasterize:YES];

And speaking of performance, it's worth noting that if you are noticing sluggish animations, you will want to use the technique of setting the shadow path after all. This update was really just to point out that setting the path is no longer required to achieve the effect of displaying both a corner radius and a shadow at the same time. If performance is your priority, though, use a path.

UPDATE 2

Since people seem to be having trouble getting this to work in some instances, I'll post a more complete code snippet here from a sample project I created:

- (void)viewDidLoad{  [super viewDidLoad];  CALayer *layer = [CALayer layer];  [layer setBounds:CGRectMake(0.0f, 0.0f, 100.0f, 200.0f)];  [layer setPosition:[[self view] center]];  [layer setBackgroundColor:[[UIColor lightGrayColor] CGColor]];  [layer setShadowOpacity:0.55f];  [layer setCornerRadius:8.0f];  [layer setBorderWidth:1.0f];  [[[self view] layer] addSublayer:layer];  [[[self testView] layer] setShadowOpacity:0.55f];  [[[self testView] layer] setShadowRadius:15.0f];  [[[self testView] layer] setCornerRadius:8.0f];  [[[self testView] layer] setBorderWidth:1.0f];}

The testView is a UIView I added in Interface Builder and set an outlet on. This is to make sure it's working the same on both layers you add explicitly as well as the layers within subviews.

I've tested this on the simulators for iOS5 through iOS6.1. It gives this result for me in each of them:

enter image description here


Because I use a UIButton with a background image none of these answers worked for me. I keep getting either no shadow or no round edges on my buttons.

The easiest way in my scenario was to just add another view behind the button and add the shadow to it like so:

button.clipsToBounds=YES;button.layer.cornerRadius = 25;UIView *shadowView = [[UIView alloc]initWithFrame:button.frame];shadowView.backgroundColor = [UIColor whiteColor];//needs this to cast shadowshadowView.layer.cornerRadius = 25;shadowView.clipsToBounds = YES;shadowView.layer.masksToBounds = NO;shadowView.layer.shadowOffset = CGSizeMake(0, 2);shadowView.layer.shadowRadius = 1;shadowView.layer.shadowOpacity = 0.2;[[button superview]addSubview:shadowView];[[button superview]bringSubviewToFront:button];


The following Swift 3 code shows how to draw a shadow and a corner radius on an image by using CAShapeLayer and CALayer.

import UIKitclass ViewController: UIViewController {    override func viewDidLoad() {        super.viewDidLoad()        // constants        let radius: CGFloat = 20, offset = 8        let rect = CGRect(x: 0, y: 0, width: 200, height: 200)        // roundedView        let roundedView = UIView()        view.addSubview(roundedView)        // shadow layer        let shadowLayer = CALayer()        shadowLayer.shadowColor = UIColor.darkGray.cgColor        shadowLayer.shadowPath = UIBezierPath(roundedRect: rect, cornerRadius: radius).cgPath        shadowLayer.shadowOffset = CGSize(width: offset, height: offset)        shadowLayer.shadowOpacity = 0.8        shadowLayer.shadowRadius = 2        roundedView.layer.addSublayer(shadowLayer)        // mask layer        let maskLayer = CAShapeLayer()        maskLayer.path = UIBezierPath(roundedRect: rect, cornerRadius: radius).cgPath        // image layer        let imageLayer = CALayer()        imageLayer.mask = maskLayer        imageLayer.frame = rect        imageLayer.contentsGravity = kCAGravityResizeAspectFill        imageLayer.contents = UIImage(named: "image")?.cgImage        roundedView.layer.addSublayer(imageLayer)        // auto layout        roundedView.translatesAutoresizingMaskIntoConstraints = false        roundedView.widthAnchor.constraint(equalToConstant: rect.width).isActive = true        roundedView.heightAnchor.constraint(equalToConstant: rect.height).isActive = true        roundedView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true        roundedView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true    }}

This code generates the following display:

enter image description here


The previous code can be refactored into the following swift files:

CustomView.swift

import UIKitclass CustomView: UIView {    var imageLayer: CALayer!    var image: UIImage? {        didSet { refreshImage() }    }    override var intrinsicContentSize:        CGSize {        return CGSize(width: 200, height: 200)    }    func refreshImage() {        if let imageLayer = imageLayer, let image = image {            imageLayer.contents = image.cgImage        }    }    override func layoutSubviews() {        super.layoutSubviews()        if imageLayer == nil {            let radius: CGFloat = 20, offset: CGFloat = 8            let shadowLayer = CALayer()            shadowLayer.shadowColor = UIColor.darkGray.cgColor            shadowLayer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: radius).cgPath            shadowLayer.shadowOffset = CGSize(width: offset, height: offset)            shadowLayer.shadowOpacity = 0.8            shadowLayer.shadowRadius = 2            layer.addSublayer(shadowLayer)            let maskLayer = CAShapeLayer()            maskLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: radius).cgPath            imageLayer = CALayer()            imageLayer.mask = maskLayer            imageLayer.frame = bounds            imageLayer.backgroundColor = UIColor.red.cgColor            imageLayer.contentsGravity = kCAGravityResizeAspectFill            layer.addSublayer(imageLayer)        }        refreshImage()    }}

ViewController.swift

import UIKitclass ViewController: UIViewController {    override func viewDidLoad() {        super.viewDidLoad()        let roundedView = CustomView()        roundedView.translatesAutoresizingMaskIntoConstraints = false        view.addSubview(roundedView)        // auto layout        let horizontalConstraint = roundedView.centerXAnchor.constraint(equalTo: view.centerXAnchor)        let verticalConstraint = roundedView.centerYAnchor.constraint(equalTo: view.centerYAnchor)        NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint])        roundedView.image = UIImage(named: "image")    }}

You can find more ways to combine images with rounded corners and shadow on this Github repo.