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