Problem:
I've implemented a collapsible header view with a curved bottom border and a gradient layer that sits above my scrollView. The header has a drop shadow that is drawn in its draw(rect:) function. The shadow gives the bottom border a glow effect. Everything looks good, but when setNeedsDisplay() is called on my header or the view is redrawn, the drop shadow goes away.
Question:
How do I make the drop shadow persist when the view is redrawn?
Code:
override func draw(_ rect: CGRect) {
super.draw(rect)
let p1 = CGPoint(x: 0, y: self.frame.height * 0.8)
let p2 = CGPoint(x: self.frame.width / 2, y: self.frame.height * 1.06)
let p3 = CGPoint(x: self.frame.width, y: self.frame.height * 0.8)
let p4 = CGPoint(x: self.frame.width, y: 0)
let p5 = CGPoint(x: 0, y: 0)
let path = UIBezierPath()
path.move(to: p1)
path.addQuadCurve(to: p3, controlPoint: p2)
path.addLine(to: p4)
path.addLine(to: p5)
path.addLine(to: p1)
path.close()
self.shapeLayer.path = path.cgPath
self.layer.mask = self.shapeLayer
if !self.didSetGradient {
self.gradient = CustomColors.blueGreenBackgroundGradient(frame: path.bounds)
self.gradient.mask = shapeLayer
self.gradient.masksToBounds = true
self.gradient.removeFromSuperlayer()
self.layer.insertSublayer(self.gradient, at: 0)
self.didSetGradient = true
}
let context = UIGraphicsGetCurrentContext()
context?.saveGState()
context?.setShadow(offset: CGSize(width: 0, height: 2.5), blur: 15, color: CustomColors.green.cgColor)
CustomColors.blueGreen.setStroke()
UIColor.clear.setFill()
path.lineWidth = 5
path.stroke()
context?.restoreGState()
}
Aight was able to figure this one out. The bezier path shadow was getting hidden when the view was redrawn because the layer mask was getting set to the shapeLayer. My solution was to draw the curved shadow using the layer's shadowPath
property and inserting the shapeLayer
as a subLayer instead of masking the layer. Make sure to add the shapeLayer
before you add the gradient, so the gradient appears on top of the shapeLayer
. The didSetGradient
block is pretty tacky as @dfd mentioned, but it's working for now!
override func draw(_ rect: CGRect) {
super.draw(rect)
let p1 = CGPoint(x: 0, y: self.frame.height * 0.8)
let p2 = CGPoint(x: self.frame.width / 2, y: self.frame.height * 1.06)
let p3 = CGPoint(x: self.frame.width, y: self.frame.height * 0.8)
let p4 = CGPoint(x: self.frame.width, y: 0)
let p5 = CGPoint(x: 0, y: 0)
let path = UIBezierPath()
path.move(to: p1)
path.addQuadCurve(to: p3, controlPoint: p2)
path.addLine(to: p4)
path.addLine(to: p5)
path.addLine(to: p1)
path.close()
self.shapeLayer.path = path.cgPath
if !self.didSetGradient {
self.layer.insertSublayer(shapeLayer, at: 0)
self.gradient = CustomColors.blueGreenBackgroundGradient(frame: path.bounds)
self.gradient.mask = shapeLayer
self.gradient.masksToBounds = true
self.layer.insertSublayer(self.gradient, at: 0)
self.didSetGradient = true
}
self.layer.shadowPath = startPath.cgPath
self.layer.shadowColor = CustomColors.blueGreen.cgColor
self.layer.shadowOpacity = 0.5
self.layer.shadowRadius = 8
self.layer.shadowOffset = CGSize(width: 0, height: 7)
self.layer.masksToBounds = false
self.clipsToBounds = false
}