I have a simple drawing routine that allows the user to draw two enclosed ovals on the screen. I want to fill the ovals with a gradient, using the inner oval to represent the "percentage" of the gradient. i.e. Th gradient will smoothly transition between the outside oval to the inside oval.
I have the interactive drawing working fine, now I just need to fill with the gradient.
Any thoughts? The docs only talk about perfectly circular gradients, not ovals.
_mike
I do not know whether it is possible oval gradient . But you can transform circle to oval. The idea is to draw a circle in transformed coordinate system.
Sample of code:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextScaleCTM(context, 1.0, 0.5);
CGGradientRef gradient;
CGColorSpaceRef colorspace;
CGFloat locations[2] = { 0.0, 1.0};
NSArray *colors = #[(id)[UIColor whiteColor].CGColor, (id)[UIColor blueColor].CGColor];
colorspace = CGColorSpaceCreateDeviceRGB();
gradient = CGGradientCreateWithColors(colorspace, (CFArrayRef)colors, locations);
CGPoint startPoint, endPoint;
CGFloat startRadius, endRadius;
startPoint.x = 180;
startPoint.y = 180;
endPoint.x = 180;
endPoint.y = 180;
startRadius = 0;
endRadius = 100;
CGContextDrawRadialGradient (context, gradient, startPoint, startRadius, endPoint, endRadius, 0);
CGContextRestoreGState(context);
}
Result of running code:
Related
I need a radial gradient in the shape of an oval or ellipse and it seems like it CGContextDrawRadialGradient can only draw a perfect circle. I've been drawing to a square context then copying/drawing into a rectangular context.
Any better way to do this?
Thanks!
The only way I've found to do this is as Mark F suggested, but I think the answer needs an example to be easier to understand.
Draw an elliptical gradient in a view in iOS (and using ARC):
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
// Create gradient
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = {0.0, 1.0};
UIColor *centerColor = [UIColor orangeColor];
UIColor *edgeColor = [UIColor purpleColor];
NSArray *colors = [NSArray arrayWithObjects:(__bridge id)centerColor.CGColor, (__bridge id)edgeColor.CGColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);
// Scaling transformation and keeping track of the inverse
CGAffineTransform scaleT = CGAffineTransformMakeScale(2, 1.0);
CGAffineTransform invScaleT = CGAffineTransformInvert(scaleT);
// Extract the Sx and Sy elements from the inverse matrix
// (See the Quartz documentation for the math behind the matrices)
CGPoint invS = CGPointMake(invScaleT.a, invScaleT.d);
// Transform center and radius of gradient with the inverse
CGPoint center = CGPointMake((self.bounds.size.width / 2) * invS.x, (self.bounds.size.height / 2) * invS.y);
CGFloat radius = (self.bounds.size.width / 2) * invS.x;
// Draw the gradient with the scale transform on the context
CGContextScaleCTM(ctx, scaleT.a, scaleT.d);
CGContextDrawRadialGradient(ctx, gradient, center, 0, center, radius, kCGGradientDrawsBeforeStartLocation);
// Reset the context
CGContextScaleCTM(ctx, invS.x, invS.y);
// Continue to draw whatever else ...
// Clean up the memory used by Quartz
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
Put in a view with a black background you get:
You can change the transform of the context to draw an ellipse (for example, apply CGContextScaleCTM(context, 2.0, 1.0) just before calling CGContextDrawRadialGradient () to draw an elliptical gradient that's twice as wide as it is high). Just remember to apply the inverse transform to your start and end points, though.
I want to accomplish a function just like a Brush. The area where finger swipes changes to trasparent with gradually changed border.
I can only change the color to crystal clear now with following codes:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(self.eraser) return;
CGFloat scale = self.transform.a;
if (scale < 1) scale = 1;
CGPoint p = [[touches anyObject] locationInView: self];
CGPoint q = [[touches anyObject] previousLocationInView: self];
UIImage* image;
image = self.image;
CGSize size = self.frame.size;
UIGraphicsBeginImageContext(size);
CGRect rect;
rect.origin = CGPointZero;
rect.size = size;
[image drawInRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineCap(context, kCGLineCapRound);
CGContextBeginPath(context);
CGContextSaveGState( context );
CGContextSetLineWidth(context, (10.0 / scale) + 1);
CGContextSetBlendMode(context, kCGBlendModeClear);
CGContextMoveToPoint(context, q.x, q.y);
CGContextAddLineToPoint(context, p.x, p.y);
CGContextStrokePath(context);
CGContextRestoreGState( context );
UIImage* editedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self setBounds:rect];
[self setImage:editedImage];
}
How can I get the edge with gradually change? Thanks in advance.
You can achieve this effect by drawing a radial gradient with a variable alpha in the kCGBlendModeDestinationIn mode in each spot the user passes.
This blend mode has the effect of only applying the layer's alpha to layers below. With the variable alpha of our gradient, we can achieve this effect.
const CGFloat kBrushSize = 10.f;
CGContextSaveGState(context);
// Make a radial gradient that goes from transparent black on the inside
// to opaque back on the outside.
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 1.0, 1.0, 1.0, 0.0,
1.0, 1.0, 1.0, 1.0 };
CGColorSpaceRef myColorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
CGGradientRef myGradient = CGGradientCreateWithColorComponents (myColorspace, components,
locations, num_locations);
CGColorSpaceRelease(myColorspace);
// Draw the gradient at the point using kCGBlendModeDestinationIn
// This mode only applies the new layer's alpha to the lower layer.
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawRadialGradient(context, myGradient, p, 0.f, p, (kBrushSize / scale) + 1, kCGGradientDrawsAfterEndLocation);
CGGradientRelease(myGradient);
CGContextRestoreGState(context);
Here is a screenshot of this code in action:
Note: Using this technique, if the user is moving his/her finger very fast, you may see a spacing effect where discrete brush dots are visible. This is a feature of some graphics software, but if this is undesirable for you, you can add code to interpolate the points between the current and last to draw more brush points, creating a more continuous stroke.
Also, you should be able to adjust the gradient color stops to achieve any kind of brush softness you like.
Source: https://developer.apple.com/library/mac/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_shadings/dq_shadings.html
I'm ultimately trying to create a gradient overlay to fade from 60% black to clear, to 40% black. The result has a lot of banding, so I'm just trying to get two colours working properly first.
In this first case, I'm trying a gradient from solid black to solid white which draws pretty much perfectly.
In the second case, I'm placing a gradient from 100% black to clear, against a solid gray background. The banding is really awful.
The code to make this gradient looks like this:
// Draw the overlay gradient
CGContextTranslateCTM(context, 0.0, size.height);
CGContextScaleCTM(context, 1.0, -1.0);
UIColor *firstColor = [UIColor colorWithWhite:0.0 alpha:1.0];
UIColor *secondColor = [UIColor clearColor];
CGContextSetBlendMode(context, kCGBlendModeSourceAtop);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSArray *coreGraphicsColors = #[(id)firstColor.CGColor, (id)secondColor.CGColor];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)coreGraphicsColors, 0);
static const CGFloat standardDegree = 270;
const CGFloat degree = (standardDegree * M_PI / 180);
const CGPoint center = CGPointMake(size.width / 2, size.height / 2);
const CGPoint startPoint = CGPointMake(center.x - cos(degree) * size.width / 2, center.y - sin(degree) * size.height / 2);
const CGPoint endPoint = CGPointMake(center.x + cos(degree) * size.width / 2, center.y + sin(degree) * size.height / 2);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
Does anyone have some experience/tips with creating these gradients which are partly transparent? I've tried altering a few things like the positions and color space, all to no avail.
This might help you AlphaGradientView
I write some code for creating the below with UIBezierPath but it didn't work. Can someone help me what's wrong with my code.
but i was tried with drawRect: it works fine....My problem is same i need in UIBezierPath as,
+ (UIBezierPath *)DropShape:(CGRect)Frame {}
my code using drawRect: is
CGContextRef context = UIGraphicsGetCurrentContext();
CGGradientRef gradient;
CGColorSpaceRef colorspace;
CGFloat locations[3] = { 0.0, 0.5, 1.0 };
NSArray *colors = #[(id)[UIColor redColor].CGColor];
colorspace = CGColorSpaceCreateDeviceRGB();
gradient = CGGradientCreateWithColors(colorspace,
(CFArrayRef)colors, locations);
CGPoint startPoint, endPoint;
CGFloat startRadius, endRadius;
startPoint.x = self.frame.size.width/2;
startPoint.y = 30;
endPoint.x = 175;
endPoint.y = 175;
startRadius = 0;
endRadius = 75;
CGContextDrawRadialGradient(context, gradient, startPoint,
startRadius, endPoint, endRadius, 0);
Thanks in advance...
There is an app called PaintCode which you can try for free.
You can draw onto a canvas and it will produce the drawRect code that you can copy and paste into your app.
I don't think you can copy from the trial version but it will create the path which you can type into your code manually.
It's worth a look.
I am using radial gradient to draw circle. It is working fine but now I want to set border color and border width to that. I tried it but it was not working.
Here is my code,
CGGradientRef gradient;
CGColorSpaceRef colorSpace;
CGFloat locations[] = {0.0,1.0};
CGFloat components[] = { red2,green2,blue2,1.0,red1,green1,blue1,1.0 };
colorSpace = CGColorSpaceCreateDeviceRGB();
gradient = CGGradientCreateWithColorComponents(colorSpace,components,locations,
sizeof(locations)/sizeof(CGFloat));
CGPoint start = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2 ), end = CGPointMake(self.bounds.size.width/2 , self.bounds.size.height/2);
CGFloat startRadius = 0.0, endRadius = radius;
CGContextDrawRadialGradient(contextRef,gradient,start,startRadius,end,endRadius,0);
CGContextSetLineWidth(contextRef, 5.0);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
How can I set border color and width to it?
First add QuartzCore Framework and then try this code...
[[Firstbtn layer]setBorderColor:[[UIColor blackColor]CGColor]];
[[self.Firstbtn layer]setBorderWidth:2.3];
[[self.Firstbtn layer]setCornerRadius:15];