Search code examples
iosswiftuiviewshadowcashapelayer

Shadow not visible on top of "sibling" view


I'm trying to create a shadow on the top a view.
This is my hierarchy:
View
------TablewView
------BottomView

TableView and BottomView have the same parent: View.
I want the BottomView to have a shadow on top of the TableView like the pic on the left, but the result is the one on the right:

enter image description here

If I try to remove the TableView I see the shadow. The BottomView haves rounded corners. This is the BottomView Class:

class BottomView: UIView {
    private var shadowLayer: CAShapeLayer!

    override func layoutSubviews() {
        super.layoutSubviews()

        if shadowLayer == nil {
            let shadowLayer = CAShapeLayer()
            shadowLayer.masksToBounds = false
            //rect is an ex.
            shadowLayer.path =  UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 200, height: 80), cornerRadius: 9).cgPath
            shadowLayer.fillColor = UIColor.red.cgColor

            shadowLayer.shadowColor = UIColor.black.cgColor
            shadowLayer.shadowPath = shadowLayer.path
            shadowLayer.shadowOffset = CGSize(width: 5, height: -5)
            shadowLayer.shadowOpacity = 1
            shadowLayer.shadowRadius = 3
            shadowLayer.zPosition = 10
            layer.insertSublayer(shadowLayer, at: 0)
            clipsToBounds = false
        }
    }
}

Solution

  • The first issue is variable / object scope...

    class BottomView: UIView {
        private var shadowLayer: CAShapeLayer!
    
        override func layoutSubviews() {
            super.layoutSubviews()
    
            if shadowLayer == nil {
                let shadowLayer = CAShapeLayer()
                ...
    

    Where you say: let shadowLayer = CAShapeLayer(), you just created a new, local shadowLayer object, and it disappears when it goes out of scope (at the end of the if block).

    Change that line to:

    shadowLayer = CAShapeLayer()   // remove the let
    

    and you should see your shadow.

    You can "automate" the sizing, by adding this below the if block:

        if shadowLayer != nil {
            shadowLayer.path =  UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height), cornerRadius: 9).cgPath
        }
    

    that will re-size the shadow layer anytime the view changes size.