Home > Software design >  CGContextSetFill with gradient
CGContextSetFill with gradient

Time:10-27

I currently have a function where I draw a QR code

  (void)drawQRCode:(QRcode *)code context:(CGContextRef)ctx size:(CGFloat)size {}

Currently, I can set the color including the colorspace using the following:

CGContextSetFillColorWithColor(ctx, [theColor colorUsingColorSpaceName:NSCalibratedWhiteColorSpace].CGColor);

I am trying to implement the ability to create a gradient I've converted two NSColors to an NSGradient - there doesn't appear to be any way of getting a gradient as an NSColor so I'm wondering what would be the best way of setting the fill of the context with a gradient?

Thanks in advance for any suggestions!

CodePudding user response:

CGContext has drawLinearGradient

https://developer.apple.com/documentation/coregraphics/cgcontext/1454782-drawlineargradient

and drawRadialGradient

https://developer.apple.com/documentation/coregraphics/cgcontext/1455923-drawradialgradient

To use them to fill a shape, put the shape's path into the context, then use the shape as a clipping path before drawing the gradient.

Here's a drawRect from a view that demonstrates the technique:

- (void) drawRect: (CGRect) rect {
    CGColorRef myColors[3] = {
        [[UIColor redColor] CGColor],
        [[UIColor yellowColor] CGColor],
        [[UIColor blueColor] CGColor] };

    CGFloat locations[3] = { 0.0, 0.25, 1.0 };

    CGRect circleRect = CGRectInset([self bounds], 20, 20);
    CGFloat circleRadius = circleRect.size.width / 2.0;

    CGContextRef cgContext = UIGraphicsGetCurrentContext();

    CGContextSaveGState(cgContext);
    CGContextSetFillColorWithColor(cgContext, [[UIColor blackColor] CGColor]);
    CGContextFillRect(cgContext, [self bounds]);
    CGContextRestoreGState(cgContext);

    CGContextSaveGState(cgContext);
    CGContextAddEllipseInRect(cgContext, circleRect);
    CGContextClip(cgContext);

    CGContextTranslateCTM(cgContext, CGRectGetMidX(circleRect), CGRectGetMidY(circleRect));
    CGContextRotateCTM(cgContext, M_PI / 3.0);


    CFArrayRef colors = CFArrayCreate(nil, (const void**) myColors, 3, &kCFTypeArrayCallBacks);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, colors, locations);
    CFRelease(colors);
    CFRelease(colorSpace);

    CGContextDrawLinearGradient(cgContext, gradient, CGPointMake(-circleRadius, 0),
                                CGPointMake(circleRadius, 0), kCGGradientDrawsAfterEndLocation   kCGGradientDrawsBeforeStartLocation);
    CFRelease(gradient);
    CGContextRestoreGState(cgContext);
}

Here's an equivalent playground in Swift.

import UIKit
import PlaygroundSupport

class CircleGradientView : UIView {
    override func draw(_ rect: CGRect) {

        let colors  = [
            UIColor.red.cgColor,
            UIColor.yellow.cgColor, // Yellow
            UIColor.blue.cgColor  // Blue
        ]

        let colorSpace = CGColorSpace(name: CGColorSpace.sRGB)!


        let locations : [CGFloat] = [0.0, 0.25, 1.0]
        let gradient = CGGradient(colorsSpace: colorSpace,
                                  colors: colors as CFArray,
                                  locations: locations)

        if let context = UIGraphicsGetCurrentContext() {
            let circleRect = self.bounds.insetBy(dx: 20, dy: 20)
            let circleRadius = circleRect.width / 2.0

            context.saveGState()
            context.addEllipse(in: circleRect)
            context.clip()

            context.translateBy(x: circleRect.midX, y: circleRect.midY)
            context.rotate(by: .pi / 3.0)

            context.drawLinearGradient(gradient!,
                                       start: CGPoint(x: -circleRadius, y: 0),
                                       end: CGPoint(x: circleRadius, y: 0),
                                       options: [.drawsAfterEndLocation, .drawsBeforeStartLocation])
           context.restoreGState()
        }
    }
}

let myView = CircleGradientView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
PlaygroundSupport.PlaygroundPage.current.liveView = myView
  • Related