This is stumping me. I have a UIView (call it "parent"). The bottommost subview of that view is a UIImageView (call it "child"), whose frame occupies the entirety of the "parent" bounds.
I want to round the corners on the "parent" view, and set a drop shadow. I do this on the CALayer
of "parent" as usual:
[[parent layer] setShadowOffset:CGSizeMake(5, 5)];
[[parent layer] setShadowRadius:6];
[[parent layer] setShadowOpacity:0.4];
[[parent layer] setCornerRadius:6];
This shows the shadow correctly, but does not round the corners.
Here's the kicker:
- If I remove the "child" image view, or shrink it so it doesn't occupy the whole bounds of the "parent" view, I get the rounded corners and shadow correctly on the parent.
- If I leave the "child" alone but set "clipsToBounds" on the "parent" view, I get the corners correctly. But now the shadow's gone.
- Setting the corner radius on the layer of the child as well seems to have no effect.
It seems like the "child" image view is just obscuring the rounded corners on the "parent" view since it takes up the whole rect, and clipping based on the parent view gets the corners but also masks off the shadow. Not sure why #3 doesn't work.
What am I missing? Have I been overlooking something obvious by staring at this too long?
Thanks.
(Shockingly, the tag "roundedcorners-dropshadow" already exists. Awesome.)
You will need two nested views, the inner one setting rounded corners and clipping to bound, and the outer view having the shadow (and therefore not clipping). In your case inner and outer view will probably be "child" and "parent", but I guess you didn't set the right clipping values for these views?
See the answer in Why masksToBounds = YES prevents CALayer shadow?.
Normally you have to set clipsToBounds to have rounded corners, but since you want to retain the shadow you have to round the corners of the shadow as well. Have you tried setting the shadow path using a bezier path? Keep clipsToBounds/masksToBounds to the default, NO. Something like:
[[parent layer] setCornerRadius:6.0f];
[[parent layer] setShadowPath:
[[UIBezierPath bezierPathWithRoundedRect:[parent bounds]
cornerRadius:6.0f] CGPath]];
Have you tried setting the bounds of the child UIImageView so that it also has rounded corners? Perhaps then it wouldn't override the shadow of the container view. Just a thought, not sure if it will work.
With Swift 3, you can choose one of the two following code snippets in order to set cornerRadius and shadow on an image view or on a view that contains an image layer.
#1. Using UIView
, CALayer
and Spring and Struts
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// constants
let radius: CGFloat = 20, dimension: CGFloat = 200, offset = 8
let frame = CGRect(x: 0, y: 0, width: 200, height: 200)
// custom view
let customView = UIView(frame: frame)
customView.contentMode = .scaleAspectFill
// image layer
let imageLayer = CALayer()
imageLayer.contentsGravity = kCAGravityResizeAspectFill
imageLayer.contents = UIImage(named: "image")!.cgImage
imageLayer.masksToBounds = true
imageLayer.frame = frame
imageLayer.cornerRadius = radius
imageLayer.masksToBounds = true
// rounded layer
let roundedLayer = CALayer()
roundedLayer.shadowColor = UIColor.darkGray.cgColor
roundedLayer.shadowPath = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: dimension, height: dimension), cornerRadius: radius).cgPath
roundedLayer.shadowOffset = CGSize(width: offset, height: offset)
roundedLayer.shadowOpacity = 0.8
roundedLayer.shadowRadius = 2
roundedLayer.frame = frame
// views and layers hierarchy
customView.layer.addSublayer(imageLayer)
customView.layer.insertSublayer(roundedLayer, below: imageLayer)
view.addSubview(customView)
// layout
customView.center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
customView.autoresizingMask = [UIViewAutoresizing.flexibleLeftMargin, .flexibleRightMargin, .flexibleTopMargin, .flexibleBottomMargin]
}
}
#2. Using UIView
, UIImageView
, CALayer
and Auto Layout
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// constants
let radius: CGFloat = 20, dimension: CGFloat = 200, offset = 8
// image view
let imageView = UIImageView(image: UIImage(named: "image"))
imageView.contentMode = .scaleAspectFill
imageView.layer.cornerRadius = radius
imageView.layer.masksToBounds = true
// rounded view
let roundedView = UIView()
roundedView.layer.shadowColor = UIColor.darkGray.cgColor
roundedView.layer.shadowPath = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: dimension, height: dimension), cornerRadius: radius).cgPath
roundedView.layer.shadowOffset = CGSize(width: offset, height: offset)
roundedView.layer.shadowOpacity = 0.8
roundedView.layer.shadowRadius = 2
// views hierarchy
roundedView.addSubview(imageView)
view.addSubview(roundedView)
// layout
imageView.translatesAutoresizingMaskIntoConstraints = false
roundedView.translatesAutoresizingMaskIntoConstraints = false
roundedView.widthAnchor.constraint(equalToConstant: dimension).isActive = true
roundedView.heightAnchor.constraint(equalToConstant: dimension).isActive = true
imageView.widthAnchor.constraint(equalTo: roundedView.widthAnchor).isActive = true
imageView.heightAnchor.constraint(equalTo: roundedView.heightAnchor).isActive = true
roundedView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
roundedView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
imageView.centerXAnchor.constraint(equalTo: roundedView.centerXAnchor).isActive = true
imageView.centerYAnchor.constraint(equalTo: roundedView.centerYAnchor).isActive = true
}
}
Both code snippets generate the following display:
You can find more ways to combine images with rounded corners and shadow on this Github repo.
If you want shadow layer with corner radius for imageview is that better solution put imageview a view as subview with 1 point margin.. and
imgBrandLogo.backgroundColor = UIColor.blue
imgBrandLogo.layer.cornerRadius = imgBrandLogo.frame.height/2
imgBrandLogo.clipsToBounds = true
viewBrandLogo.layer.shadowColor = UIColor(rgb:0x262626,alpha:0.24).cgColor
viewBrandLogo.layer.shadowOffset = CGSize(width: 0, height: 1)
viewBrandLogo.layer.shadowOpacity = 1
viewBrandLogo.layer.shadowPath = UIBezierPath(roundedRect:imgBrandLogo.bounds , cornerRadius: imgBrandLogo.frame.height/2).cgPath
viewBrandLogo.backgroundColor = UIColor.clear.withAlphaComponent(0.0)