leedex
leedex

Reputation: 1013

Add shadow to UIView whose shape has been changed by UIBezierPath

I've created an extension for UIView that allows me to make a concave shape.

extension UIView {
    func createConcave(depth: CGFloat) {
    let width = self.bounds.width
    let height = self.bounds.height

    let path = UIBezierPath()
    let p0 = CGPoint(x: 0, y: 0)
    let p2 = CGPoint(x: width, y: 0)
    let p1 = CGPoint(x: width / 2, y: depth)
    path.move(to: p0)
    path.addQuadCurve(to: p2, controlPoint: p1)
    path.addLine(to: CGPoint(x: width, y: height))
    path.addLine(to: CGPoint(x: 0, y: height))
    path.addLine(to: p0)
    let mask = CAShapeLayer()
    mask.path = path.cgPath
    self.layer.mask = mask
    self.layer.masksToBounds = false
    }
}

The shape of the UIView after implementing concavity

What would be a good solution to add a shadow to the view that matches the shape? Would I have to specify the shadow path to be the same path as the concave shape?

Upvotes: 1

Views: 115

Answers (1)

Rob
Rob

Reputation: 437552

You are masking the layer to the path. Thus anything, including the shadow, will be clipped by that mask.

Instead of masking, add sublayer.

E.g.

@IBDesignable
class ConcaveView: UIView {
    @IBInspectable var depth: CGFloat = 10         { didSet { updatePath() } }
    @IBInspectable var fillColor: UIColor = .red   { didSet { shapeLayer.fillColor = fillColor.cgColor } }

    private lazy var shapeLayer: CAShapeLayer = {
        let shapeLayer = CAShapeLayer()
        shapeLayer.fillColor = fillColor.cgColor
        shapeLayer.shadowColor = UIColor.black.cgColor
        shapeLayer.shadowRadius = 5
        shapeLayer.shadowOpacity = 1
        shapeLayer.shadowOffset = .zero
        return shapeLayer
    }()

    override init(frame: CGRect = .zero) {
        super.init(frame: frame)
        configure()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configure()
    }

    func configure() {
        layer.addSublayer(shapeLayer)
        clipsToBounds = false
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        updatePath()
    }

    func updatePath() {
        let path = UIBezierPath()
        let point0 = CGPoint(x: bounds.minX, y: bounds.minY)
        let point2 = CGPoint(x: bounds.maxX, y: bounds.minY)
        let point1 = CGPoint(x: bounds.width / 2, y: bounds.minY + depth)
        path.move(to: point0)
        path.addQuadCurve(to: point2, controlPoint: point1)
        path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
        path.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY))
        path.addLine(to: point0)

        shapeLayer.path = path.cgPath
    }
}

That yields:

enter image description here

Upvotes: 4

Related Questions