1

When trying to draw a radial gradient from white to clear, I obtain different results depending on whether I use UIColor(white:alpha:), UIColor.white.withAlpha.Components(alpha:) or UIColor.color. How Can I get the same gradient with a clear color as with a plain color?

I order to draw a radial gradient, I have overridden the draw(context:) method (see below). My code seems to work fine when using plain colours for a gradient but works "mysteriously" when using a clear color.

override func draw(in ctx: CGContext) {
    super.draw(in: ctx)

    ctx.saveGState()
    let gradient                = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: colorFill as CFArray, locations: self.locations as! [CGFloat] )
    let startPoint              = CGPoint(x: startX, y: startY )
    let endPoint                = CGPoint(x: endX , y: endY)
    let drawingOption           : CGGradientDrawingOptions = radius > 0 ? [] : .drawsAfterEndLocation

    //self.masksToBounds          = true
    if gradientType == .radial {
        ctx.drawRadialGradient(gradient!, startCenter: startPoint, startRadius: 0.0, endCenter: endPoint, endRadius: self.frame.width / 2, options: drawingOption)
    } else {
        self.startPoint = startPoint
        self.endPoint   = endPoint
    }
}

Here's the results I obtain depending on the input colours for the gradient:

[UIColor.white.cgColor,UIColor.black.cgColor] (desired result with clear color instead of black)

White to Black

[UIColor.white.cgColor, UIColor.clear.cgColor]

White to Clear

[UIColor.white.cgColor, UIColor.white.withAlphaComponent(0.0).cgColor]

White to White with alpha component

[UIColor(white: 0.0, alpha: 1).cgColor, UIColor(white: 1.0, alpha: 0).cgColor]

Gray layer with alpha component

Could someone can explain why I don't get the same output as with only plain colours (output I wish to obtain)?

Thanks a lot for taking the time to read and reply!

Chris
  • 305
  • 1
  • 12
  • Hope it helps -> https://stackoverflow.com/questions/31853859/radial-gradient-background-in-swift – dahiya_boy Dec 28 '18 at 13:05
  • Thanks for your reply @dahiya_boy. I already knew this topic and my draw method is basically the same already. – Chris Dec 28 '18 at 13:13
  • 2
    **Problem solved!** I will post an update later but to put it all in a nutshell, I came to have the confirmation that my layer was drawn twice (you can see a bit of white in the black and white picture) as I actually suspected. In my `init' method I was setting `self.colors` and `self.locations` which in fact draws the layer before the `draw(in context)` method can _redraw_ it. By way of saving the info in separate type properties, I can prevent the first drawing of the layer, hence getting the adequate result! – Chris Dec 28 '18 at 13:56

1 Answers1

1

The layer is not drawn only in the draw(in context:) method. This line of code confirms some properties had been set prior to the call of the method, which has priorly triggered the drawing of the layer (self.locations):

CGColorSpaceCreateDeviceRGB(), colors: colorFill as CFArray, locations: self.locations as! [CGFloat] )

By removing the set of self.colors and self.locations properties in the custom initializer and storing the values in Type Properties instead before calling self.setNeedDisplay(), the layer is only being drawn in the draw(in context:) method.

class CustomLayer {
    override init() {
        super.init()
        needsDisplayOnBoundsChange = true
    }
    
    override init(layer: Any) {
        super.init(layer: layer)
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    var colors    = []()
    var colorMap  = [NSNumber]()
    
    custom init() {
      colors = [color,color]
      colorMap = [0.0, 1.0]
      self.setNeedDisplay()
    }
    
    override func draw(in ctx: CGContext) {
        super.draw(in: ctx)
        ctx.saveGState()
        guard let gradient          = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: colors as CFArray, locations: colorMap as! [CGFloat] ) else {return}
        let startPoint              = CGPoint(x: startX, y: startY )
        let endPoint                = CGPoint(x: endX , y: endY)
        self.masksToBounds          = true
        if gradientType == .radial {
            ctx.drawRadialGradient(gradient, startCenter: startPoint, startRadius: 0.0, endCenter: endPoint, endRadius: self.frame.width / 2, options: drawingOption)
        } else {
            self.locations          = colorMap
            self.colors             = colors
            self.startPoint         = startPoint
            self.endPoint           = endPoint
        }
    }
}

enter image description here

Cheers!

Chris
  • 305
  • 1
  • 12