Gradient with setFill()?

Hello.


I subclassed a UIView to do some drawing. Inside the view, I created a frame that takes only a part of the view.


  override func drawRect(rect: CGRect)
    {
        self.rectangleFrame = CGRectMake(0,20,380,580)
        ...
    }


At the moment, the frame is filled with blue color:


let frameBezierPath = UIBezierPath(roundedRect: self.rectangleFrame, cornerRadius: 5)
UIColor.blueColor().setFill()
frameBezierPath.fill()


Instead, I want to fill the frame with gradient color. Usually, the way I do it is using a context:


let startColor = UIColor(red: 30/255, green: 65/255, blue: 255/255, alpha: 1.0)
let endColor = UIColor(red: 204/255, green: 255/255, blue: 255/255, alpha: 1.0)
let locations : [CGFloat] = [0, 1.0]
let colorspace = CGColorSpaceCreateDeviceRGB()
let colors = [startColor.CGColor, endColor.CGColor]

let gradient = CGGradientCreateWithColors(colorspace, colors, locations)
let startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
let endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
       
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, CGGradientDrawingOptions(rawValue: 0))

CGContextSaveGState(context);
if (!CGContextIsPathEmpty(context))
{
  CGContextClip(context);
}
       
CGContextRestoreGState(context);


However, in my case I cannot do, since it fills the whole UIView with the gradient color, and I just want to limit it to the frame only. I am a bit confused how to use setFill() with gradient.


Thank you!

Accepted Reply

You need to set a clip path (a rectangle or rounded rectangle in this case), before you draw the gradient.


Your axis start and end points look fine. The normal operation of the gradient fill will be to fill to the whole view.


Best Wishes,

. . . . . . . . Henry

Replies

You write that you wish to replace the solid color fill with a gradient. Are you still setting up the rounded rectangle prior to drawing the gradient?

If you don't create the rounded rectangle, there will be no path, and so there won't be any clipping.


There's no such thing as setFill with a gradient. The way that you are doing the gradient drawing looks reasonable, given that I don't know if you actually have set up a clip path.


If that's not the case, there's something else not quite right.


Best WIshes,


. . . . . . . . Henry

Looking at your code you use a CGRect variable "rect" to define the startPoint and endPoint. I am not sure where you define that CGRect variable. I think it should be replaced with "self.rectangleFrame".

Thank you, guys.


Just wanted to show you the kind of issue here for me. If I use the following code:


let frameBezierPath = UIBezierPath(roundedRect: self.rectangleFrame, cornerRadius: 5)

        let startColor = UIColor(red: 30/255, green: 65/255, blue: 255/255, alpha: 1.0)
        let endColor = UIColor(red: 204/255, green: 255/255, blue: 255/255, alpha: 1.0)
        let locations : [CGFloat] = [0, 1.0]
        let colorspace = CGColorSpaceCreateDeviceRGB()
        let colors = [startColor.CGColor, endColor.CGColor]
  
        let gradient = CGGradientCreateWithColors(colorspace, colors, locations)
        let startPoint = CGPointMake(CGRectGetMidX(self.rectangleFrame), CGRectGetMinY(self.rectangleFrame));
        let endPoint = CGPointMake(CGRectGetMidX(self.rectangleFrame), CGRectGetMaxY(self.rectangleFrame));
  
        CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, CGGradientDrawingOptions(rawValue: 0))
  
        CGContextSaveGState(context);
        if (!CGContextIsPathEmpty(context))
        {
            CGContextClip(context);
        }
  
        CGContextRestoreGState(context);


I get this image:


https://www.dropbox.com/s/m1weqd4ne574dp2/IMG_0040.jpg?dl=0


The context is the whole UIView and is wider than the frame in which I want to draw gradient.


If I do this:


let frameBezierPath = UIBezierPath(roundedRect: self.rectangleFrame, cornerRadius: 5)
        UIColor.blueColor().setFill()
        frameBezierPath.fill()


I get the following:


https://www.dropbox.com/s/pwvn9ygoa03f64w/IMG_0041.jpg?dl=0


I need to get the second image but with the gradient.


Thanks a lot!

You need to set a clip path (a rectangle or rounded rectangle in this case), before you draw the gradient.


Your axis start and end points look fine. The normal operation of the gradient fill will be to fill to the whole view.


Best Wishes,

. . . . . . . . Henry

Solved it by doing this:


context.saveGState();
       
        context.beginPath()
        context.addRect(self.rectangleFrame)
        context.closePath()
       
        if (!context.isPathEmpty)
        {
            context.clip()
        }
       
        let startColor = UIColor(red: 30/255, green: 65/255, blue: 255/255, alpha: 1.0)
        let endColor = UIColor(red: 204/255, green: 255/255, blue: 255/255, alpha: 1.0)
        let locations : [CGFloat] = [0, 1.0]
        let colorspace = CGColorSpaceCreateDeviceRGB()
        let colors = [startColor.cgColor, endColor.cgColor]
       
        let gradient = CGGradient(colorsSpace: colorspace, colors: colors, locations: locations)
        let startPoint = CGPoint(x: self.rectangleFrame.midX, y: self.rectangleFrame.minY);
        let endPoint = CGPoint(x: self.rectangleFrame.midX, y: self.rectangleFrame.maxY);
       
        context.drawLinearGradient(gradient!, start: startPoint, end: endPoint, options: CGGradientDrawingOptions(rawValue: 0))

        context.restoreGState();


Thank you all.