1

I'm adding ShapeView which is subclass of UIView to View Controller in which I'm drawing Bezier Path and the background of that view always stays black. I tried to fix my problem with answers from UIView Background Color Always Black and Cant Change UIView Background Color From Black but, unfortunately, no results.

The gradient green is shape, and the black areas under shape should be white.

enter image description here

Here is code from ShapeView class.

class ShapeView: UIView {

    //// Color Declarations
    // Green - Storage
    let gradientColor0 = UIColor(red: 0.082, green: 0.608, blue: 0.486, alpha: 1.000)
    let gradientColor1 = UIColor(red: 0.502, green: 0.980, blue: 0.949, alpha: 1.000)

    override init(frame: CGRect) {
        super.init(frame: frame)

        self.isOpaque = false
    }

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

    override func draw(_ rect: CGRect) {
        super.draw(rect)

        //// General Declarations
        let context = UIGraphicsGetCurrentContext()!

        //// Gradient Declarations
        let paint0_linear2 = CGGradient(colorsSpace: nil, colors: [gradientColor0.cgColor, gradientColor1.cgColor] as CFArray, locations: [0, 1])!

        //// Bezier Drawing
        let bezierPath = UIBezierPath()
        bezierPath.move(to: CGPoint(x: 0, y: 342))
        bezierPath.addLine(to: CGPoint(x: 187.5, y: 372))
        bezierPath.addLine(to: CGPoint(x: 375, y: 342))
        bezierPath.addLine(to: CGPoint(x: 375, y: 0))
        bezierPath.addLine(to: CGPoint(x: 0, y: 0))
        bezierPath.addLine(to: CGPoint(x: 0, y: 342))
        bezierPath.close()
        bezierPath.usesEvenOddFillRule = true
        context.saveGState()
        bezierPath.addClip()
        context.drawLinearGradient(paint0_linear2,
                                   start: CGPoint(x: 363.75, y: -664.71),
                                   end: CGPoint(x: 900.13, y: 234.82),
                                   options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
        context.restoreGState()
    }
}

Any ideas what to do?

T.Jurko
  • 152
  • 11

2 Answers2

2

I think you can add those two lines to func draw(_ rect: CGRect) just after super.draw(rect):

UIColor.white.setFill()
UIRectFill(rect)

so the method looks like this:

override func draw(_ rect: CGRect) {
    super.draw(rect)

    UIColor.white.setFill()
    UIRectFill(rect)

    //// General Declarations
    let context = UIGraphicsGetCurrentContext()!



    //// Gradient Declarations
    let paint0_linear2 = CGGradient(colorsSpace: nil, colors: [gradientColor0.cgColor, gradientColor1.cgColor] as CFArray, locations: [0, 1])!

    //// Bezier Drawing
    let bezierPath = UIBezierPath()
    bezierPath.move(to: CGPoint(x: 0, y: 342))
    bezierPath.addLine(to: CGPoint(x: 187.5, y: 372))
    bezierPath.addLine(to: CGPoint(x: 375, y: 342))
    bezierPath.addLine(to: CGPoint(x: 375, y: 0))
    bezierPath.addLine(to: CGPoint(x: 0, y: 0))
    bezierPath.addLine(to: CGPoint(x: 0, y: 342))
    bezierPath.close()
    bezierPath.usesEvenOddFillRule = true
    context.saveGState()
    bezierPath.addClip()
    context.drawLinearGradient(paint0_linear2,
                               start: CGPoint(x: 363.75, y: -664.71),
                               end: CGPoint(x: 900.13, y: 234.82),
                               options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
    context.restoreGState()
}

If you need other background-colour you can change white in: UIColor.white.setFill() to needed colour.

Maciej Gad
  • 1,701
  • 16
  • 21
2

You may find this much easier to work with...

@IBDesignable
class JurkoShapeView: UIView {

    override open class var layerClass: AnyClass {
        return CAGradientLayer.classForCoder()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

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

    func commonInit() -> Void {

        if let l = layer as? CAGradientLayer {

            let gradientColor0 = UIColor(red: 0.082, green: 0.608, blue: 0.486, alpha: 1.000)
            let gradientColor1 = UIColor(red: 0.502, green: 0.980, blue: 0.949, alpha: 1.000)

            l.colors = [gradientColor0.cgColor, gradientColor1.cgColor]
            l.startPoint = CGPoint(x: 0.0, y: 0.0)
            l.endPoint = CGPoint(x: 0.0, y: 1.0)
        }

    }

    override func layoutSubviews() {
        super.layoutSubviews()

        let l = CAShapeLayer()

        let y1 = bounds.size.height - 30

        let bezierPath = UIBezierPath()
        bezierPath.move(to: CGPoint(x: 0, y: 0))
        bezierPath.addLine(to: CGPoint(x: bounds.maxX, y: 0))
        bezierPath.addLine(to: CGPoint(x: bounds.maxX, y: y1))
        bezierPath.addLine(to: CGPoint(x: bounds.midX, y: bounds.maxY))
        bezierPath.addLine(to: CGPoint(x: 0, y: y1))
        bezierPath.close()

        l.path = bezierPath.cgPath

        self.layer.mask = l

    }
}

By making it @IBDesignable you can see it while designing in Storyboard:

enter image description here

DonMag
  • 69,424
  • 5
  • 50
  • 86
  • I was about to add a comment to my response that it is better to use `CAGradientLayer` and mask it with `CAShapeLayer`, but @DonMag already did it. – Maciej Gad Oct 28 '19 at 18:32
  • What is the meaning of `lazy var gradientLayer`? I mean you never used it. – Leo Dabus Oct 28 '19 at 20:57
  • @LeoDabus - whoops... was from an older approach (that got left in)... edited my answer and removed it. – DonMag Oct 28 '19 at 21:15
  • Thank you for your answer @DonMag . I rewrited my code to your version, It's much easier to use and you saved me time for fixing Bezier drawing from static points to dynamic points depended by view size. – T.Jurko Oct 29 '19 at 10:18