重绘视图时删除 draw(rect:) 中 uibezierpath 上设置的阴影

Shadow set on uibezierpath in draw(rect:) removed when view is redrawn

问题:

我实现了一个可折叠的 header 视图,该视图具有弯曲的底部边框和位于我的 scrollView 上方的渐变层。 header 有一个在其 draw(rect:) 函数中绘制的投影。阴影使底部边框具有发光效果。一切看起来都不错,但是当在我的 header 上调用 setNeedsDisplay() 或重绘视图时,投影消失了。

问题:

如何在重绘视图时使阴影保持不变?

代码:

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 能够解决这个问题。重绘视图时贝塞尔曲线路径阴影被隐藏,因为图层蒙版被设置为 shapeLayer。我的解决方案是使用层的 shadowPath 属性 绘制弯曲阴影并将 shapeLayer 作为子层插入而不是遮盖层。确保在添加渐变之前添加 shapeLayer,以便渐变显示在 shapeLayer 之上。正如@dfd 提到的那样,didSetGradient 块非常俗气,但它现在可以工作了!

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
}