0

Hello i have some views with rounded corners, and I'd like to apply a shadow to this views.

SO first I round the view :

view.layer.cornerRadius = 15
view.clipsToBounds = true

then I apply the shadow :

    func dropShadow(scale: Bool = true) {
        self.layer.masksToBounds = false
        self.layer.cornerRadius = 15
        self.layer.shadowColor = UIColor.black.cgColor
        self.layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius).cgPath
        self.layer.shadowOffset = CGSize(width: 3, height: 3)
        self.layer.shadowOpacity = 0.5
        self.layer.shadowRadius = 5
    }
view.dropShadow()

I got my rounded view with a shadow but the shadow is not rounded like my view. The shadow is not rounded at all

Cydiaddict
  • 277
  • 4
  • 17
  • Can you show a screenshot of the shadow? – Sweeper Aug 04 '19 at 02:05
  • [For example](https://stackoverflow.com/questions/52431760/how-to-round-a-shadow-in-ios/52432975#52432975) and [example](https://stackoverflow.com/questions/54728873/how-to-apply-corner-radius-to-certain-corners-and-add-shadow/54729120#54729120) – MadProgrammer Aug 04 '19 at 02:06
  • link to the screenshot, @Sweeper https://hebergeur-images.com/up/82deddb900db73476dabea581422f50d.jpeg – Cydiaddict Aug 04 '19 at 02:20
  • That seems like your view is the subview of another view, which is covering up the shadows. Make the shadow radius smaller, and add some space between your view and its parent view. Also try not setting `shadowPath`, which causes it to use a default one. – Sweeper Aug 04 '19 at 02:24
  • i have the problem on the 3 views, look the storyboard on that image : https://hebergeur-images.com/vi/1aee842741c651f775956b09acb53750 – Cydiaddict Aug 04 '19 at 02:41

2 Answers2

2

You cannot cast a shadow from a view whose clipsToBounds is true. If a view's masksToBounds is true, its clipsToBounds is true; they are the same thing.

If you want a shadow to appear to come from from a view that clips, you need to use two views: one that the user can see, with rounded corners and clipsToBounds set to true, and another that the user can't see because it's behind the first one, also with rounded corners, but with clipsToBounds set to false, to cast the shadow.

enter image description here

class ShadowView : UIView {
    override init(frame: CGRect) {
        super.init(frame:frame)
        self.isOpaque = true
        self.backgroundColor = .black
        self.dropShadow()
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    func dropShadow() {
        self.layer.masksToBounds = false
        self.layer.cornerRadius = 15
        self.layer.shadowColor = UIColor.black.cgColor
        self.layer.shadowOffset = CGSize(width: 3, height: 3)
        self.layer.shadowOpacity = 0.5
        self.layer.shadowRadius = 5
    }
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let r = CGRect(x: 100, y: 100, width: 100, height: 100)
        let v = UIImageView(frame:r)
        v.image = UIImage(named:"marsSurface.jpg")
        v.clipsToBounds = true
        v.backgroundColor = .red
        v.layer.cornerRadius = 15
        self.view.addSubview(ShadowView(frame:r))
        self.view.addSubview(v)
    }
}

Note that neither view is a subview of the other, nor do they have a superview that clips, as that would clip the shadow.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • I tried this : ``` for view in self.views { view!.layer.cornerRadius = 15 view!.clipsToBounds = false view!.layer.masksToBounds = false view!.layer.shadowColor = UIColor.black.cgColor view!.layer.shadowPath = UIBezierPath(roundedRect: view!.bounds, cornerRadius: view!.layer.cornerRadius).cgPath view!.layer.shadowOffset = CGSize(width: 3, height: 3) view!.layer.shadowOpacity = 0.5 view!.layer.shadowRadius = 5 } ```and still get not rounded shadow – Cydiaddict Aug 05 '19 at 13:13
  • Shrug. I showed you the entirety of my code and I showed you the result. If you want the result I got, do what I did. You are doing something else so you get a different result. – matt Aug 05 '19 at 13:17
  • Your shadow _is_ rounded. But then it is [clipped](https://hebergeur-images.com/up/82deddb900db73476dabea581422f50d.jpeg) because you didn't listen to what I said in the last paragraph of my answer. The problem is not your shadow rounding; the problem is that the _superview_ of your shadow view is limiting the shadow to its own rectangle. – matt Aug 05 '19 at 13:19
  • you said me if clipsTobounds is set to false I can see round shadow. That's what I did but still not rounded. – Cydiaddict Aug 05 '19 at 18:54
  • and I don't use direct but storyboard, this is what I get with your code : https://hebergeur-images.com/vi/0cc2422b41193c82dfe584e57b99f51e – Cydiaddict Aug 05 '19 at 19:16
0

Trying to have a single view handle both corner rounding and shadow logic is not recommended. The best way to create the effect you are looking for is to wrap your rounded view with another parent view that owns the shadow.

This can be best illustrated with a playground. Try this code out in a playground.

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {
  override func loadView() {
    let view = UIView()
    view.backgroundColor = .white

    let shadowView = UIView()
    shadowView.frame = CGRect(x: 50, y: 200, width: 200, height: 100)
    shadowView.backgroundColor = nil
    shadowView.layer.shadowColor = UIColor.black.cgColor
    shadowView.layer.shadowOffset = CGSize(width: 0, height: 4.0)
    shadowView.layer.shadowRadius = 8.0
    shadowView.layer.shadowOpacity = 1

    let roundedView = UIView()
    roundedView.frame = shadowView.bounds
    roundedView.backgroundColor = .gray
    roundedView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner,.layerMaxXMinYCorner]
    roundedView.layer.cornerRadius = 10
    roundedView.layer.masksToBounds = true

    shadowView.addSubview(roundedView)
    view.addSubview(shadowView)

    view.backgroundColor = UIColor.white
    self.view = view
  }
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

Don't forget to set the shadowPath property on your shadow view's layer in either viewWillLayoutSubviews or layoutSubviews. If this property is set to nil, UIKit has to perform off screen rendering to figure out how to draw the shadow rect.

chen
  • 1
  • Please give me code I can use on my views directly, a UIView extension if possible. I tried your playground but nothing happened. – Cydiaddict Aug 05 '19 at 01:04