Can't set cornerRadius AND shadow on layer that has an image view stretched to its bounds?
Asked Answered
P

5

18

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:

  1. 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.
  2. If I leave the "child" alone but set "clipsToBounds" on the "parent" view, I get the corners correctly. But now the shadow's gone.
  3. 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.)

Pittsburgh answered 14/9, 2010 at 2:10 Comment(0)
B
23

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?.

Beira answered 29/9, 2011 at 19:56 Comment(1)
At long last, we have a winner. :) Thanks Julian.Pittsburgh
S
5

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]];
Salisbury answered 20/9, 2010 at 15:45 Comment(4)
Thanks Matt. Yes-- the rounding of the shadow corners isn't a problem-- they work whether I set it with a path or with the regular properties. I just can't get the shadow to show up at all when I set clipsToBounds, and if I don't set it, the image view kills the rounded corners. (Note that if I don't have the "child" image view at all, both work as expected)Pittsburgh
What happens if instead of using an image view you just set your layer's contents to the image you want? e.g. [layer setContents:(id)[image CGImage]]; You can setContentsGravity:kCAGravityResizeAspectFill to get the image to fill in the layer with aspect fill or try other constants there if that's not the desired effect.Salisbury
In my case (quite similar), I have a XIB file which has a UIImageView at the bottom of the stack which has the background of the item being displayed. The image view is overriding the corner radius of its parent. Don't know how to do the content setting stuff you suggest Matt, since it's a xib file. (not OP but same problem)Alver
@Kalle. I'm not sure what you're asking exactly. Can you post it as a new question that cites this one and then explain in more depth what's not working for you? Maybe include some code? Thanks.Salisbury
M
0

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.

Minus answered 26/9, 2010 at 13:36 Comment(0)
R
0

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:

enter image description here


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

Rhino answered 3/1, 2016 at 22:1 Comment(0)
R
0

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)
Rosio answered 6/1, 2019 at 6:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.