I am trying to apply an alpha gradient to a UIView but can't get it to work at all. My current drawRect: method looks like this -
- (void)drawRect:(CGRect)rect
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGGradientRef alphaGradient;
CGColorSpaceRef rgbColorSpace;
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 1.0, 1.0, 1.0, 1.0, // Start color
1.0, 1.0, 1.0, 0.0 }; // End color
rgbColorSpace = CGColorSpaceCreateDeviceRGB();
alphaGradient = CGGradientCreateWithColorComponents(rgbColorSpace, components, locations, num_locations);
CGRect currentBounds = self.bounds;
CGPoint bottomCenter = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMaxY(currentBounds));
CGPoint midCenter = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMidY(currentBounds));
CGContextDrawLinearGradient(currentContext, alphaGradient, midCenter, bottomCenter, 0);
CGGradientRelease(alphaGradient);
CGColorSpaceRelease(rgbColorSpace);
}
I am sure there is something I am missing here. I have tried changing the RGB values to 0.0 instead of 1.0, changing the color space to gray scale, etc.
Any help pointing me in the right direction would be much appreciated. Thanks
I resolved the issue by making sure the view's background color was set to clear, then applying the view's layer as a mask on the image view.
I use CAGradient:
UIView * viewToGradient = // view to add gradient
UIColor * color1 = [UIColor lightGrayColor]; // gradient color 1
UIColor * color2 = [UIColor clearColor]; // gradient color 2
CAGradientLayer * gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = viewToGradient.bounds;
gradientLayer.colors = [NSArray arrayWithObjects:(id)color1.CGColor, (id)color2.CGColor, nil];
gradientLayer.opacity = .6;
[viewToGradient.layer addSublayer:gradientLayer];
Related
It should be very simple to create a black to white radial gradient using Core Graphics, but even after many tries I am not able to figure where I am going wrong.
I want something like this:
This is my code:
CGFloat radius = shapeRect.size.width/2.0;
CGPoint center = CGPointMake(shapeRect.origin.x+shapeRect.size.width/2, shapeRect.origin.y+shapeRect.size.height/2);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGContextRef context = CGBitmapContextCreate(NULL, shapeRect.size.width, shapeRect.size.height, 8, shapeRect.size.width*4, colorSpace, kCGImageAlphaNone);
CGFloat components[] = {0.0, 1.0};
CGFloat locations[] = {0.0, 1.0};
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, 2);
CGContextDrawRadialGradient(context, gradient, center, 10, center, radius, 0);
CGImageRef img = CGBitmapContextCreateImage(context);
[maskedView setImage:[UIImage imageWithCGImage:img]];
CGGradientRelease(gradient);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
ShapeRect here has self.view bounds.
I am getting a whole black screen with no gradient as output.
I am using iPhone 7 simulator.
Your suggestions can save my day
Thank you.
Please check this snippet. You can change components and locations values to get what you want and to understand how the drawing works. Refer to CGGradientCreateWithColorComponentsdescription
#import "TestView.h"
#implementation TestView {
CGGradientRef _gradient;
}
-(void)drawRect:(CGRect)rect
{
CGFloat radius = self.frame.size.width / 2.0;
CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
CGContextRef context = UIGraphicsGetCurrentContext();
CGGradientRef gradient = [self createGradient];
CGContextDrawRadialGradient(context, gradient, center, 10, center, radius, kCGGradientDrawsBeforeStartLocation);
}
-(CGGradientRef)createGradient
{
if (!_gradient) {
CGFloat components[] = { 0.0, 1.0, 0.5, 0.5, 1.0, 0.3 };
CGFloat locations[] = { 0.0, 0.6, 1.0 };
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
_gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, 3);
CGColorSpaceRelease(colorSpace);
}
return _gradient;
}
- (void)layoutSubviews
{
self.backgroundColor = [UIColor whiteColor];
self.layer.borderColor = [UIColor lightGrayColor].CGColor;
self.layer.borderWidth = 1.;
}
#end
Result:
my code of making CAShapeLayer is
UIBezierPath *circle = [UIBezierPath bezierPathWithArcCenter:CGPointMake(75, 125)
radius:50
startAngle:0
endAngle:1.8 * M_PI
clockwise:YES];
CAShapeLayer *circleLayer = [CAShapeLayer layer];
[circleLayer setFrame:CGRectMake(200 - 50, 300 - 50, 100, 100)];
circleLayer.path = circle.CGPath;
circleLayer.bounds = CGPathGetBoundingBox(circle.CGPath);
circleLayer.strokeColor = [UIColor colorWithRed:0.4 green:1.0 blue:0.2 alpha:0.5].CGColor;
[circleLayer setFillColor:[UIColor clearColor].CGColor];
circleLayer.lineWidth = 3.0;
if ([circleLayer animationForKey:#"SpinAnimation"] == nil) {
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
animation.fromValue = [NSNumber numberWithFloat:0.0f];
animation.toValue = [NSNumber numberWithFloat: 2 * M_PI];
animation.duration = 2.0f;
animation.repeatCount = INFINITY;
animation.removedOnCompletion = NO;
[circleLayer addAnimation:animation forKey:#"SpinAnimation"];
}
[self.view.layer addSublayer:circleLayer];
I want to make CAGradientLayer instead of CAShapeLayer.
REASON: I need to use gradient color in layer, which is not possible in CAShapeLayer.
I want to use yellow to black gradient upon Cirle.
Image :
In my required output, color is fetched from background image, and put some black at the end, or opacity 0 within layer.
Any Idea, suggestion.
Thanks.
One quick and dirty trick is adding a gradient layer as a sublayer then put some more layers with the background colors same as the superview (in the original question, it's white).
For example:
Add a gradient layer (and set proper cornerRadius).
Add a smaller layer with white background color at the center of the gradient layer (this will create a ring).
Create a small segment of arc with white background color.
However, this may not be suitable views with background images.
To solve this, you can create a custom subclass of UIView and override the drawRect method.
Suppose there are instance variables: startColor, endColor, radius, strokeWidth, and mirrored.
The variable mirrored is used to control the position of the gap (cut off left side or right side) and the rotation direction of the animation (clockwise or counter-clockwise).
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetBlendMode(ctx, kCGBlendModeNormal);
CGFloat gradientColors[4 * 2];
extractColorComponents(_startColor, gradientColors, 0);
extractColorComponents(_endColor, gradientColors, 4);
CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, gradientColors, NULL, 2);
CGColorSpaceRelease(baseSpace);
baseSpace = nil;
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint, 0);
CGGradientRelease(gradient);
gradient = nil;
// fill 'clear' color
CGContextSetFillColorWithColor(ctx, [UIColor clearColor].CGColor);
CGContextSetStrokeColorWithColor(ctx, [UIColor clearColor].CGColor);
CGContextSetBlendMode(ctx, kCGBlendModeClear);
CGFloat innerCircleRadius = _radius - _strokeWidth;
// fill an 'empty' hole inside the gradient part
CGContextFillEllipseInRect(ctx, (CGRect){
{_strokeWidth, _strokeWidth},
{innerCircleRadius * 2, innerCircleRadius * 2}
});
// fill an 'empty' segment of arc
CGFloat startAngle = _mirrored ? (0.9 * M_PI) : (-0.1 * M_PI);
CGFloat endAngle = startAngle + 0.2 * M_PI;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:(CGPoint){_radius, _radius}
radius:_radius - _strokeWidth * 0.5
startAngle:startAngle
endAngle:endAngle
clockwise:YES];
path.lineWidth = _strokeWidth;
CGContextAddPath(ctx, path.CGPath);
CGContextSetLineWidth(ctx, _strokeWidth + 2);
CGContextStrokePath(ctx);
CGContextSetBlendMode(ctx, kCGBlendModeNormal);
}
Here's the complete implementation of the view.
Final result:
Two rings:
You can add two rings in your viewDidLoad or somewhere else with the following code to achieve the desired effect:
- (void)viewDidLoad
{
[super viewDidLoad];
UIColor *startColor = [UIColor colorWithHue:0.02 saturation:0.74 brightness:0.91 alpha:1];
UIColor *endColor = [UIColor colorWithHue:0.57 saturation:0.76 brightness:0.86 alpha:1];
CGPoint center = CGPointMake(160, 200);
Spinner *outerSpinner = [[Spinner alloc] initWithCenter:center
radius:50
strokeWidth:3
startColor:startColor
endColor:endColor
mirrored:NO];
Spinner *innerSpinner = [[Spinner alloc] initWithCenter:center
radius:40
strokeWidth:3
startColor:startColor
endColor:endColor
mirrored:YES];
[self.view addSubview:outerSpinner];
[self.view addSubview:innerSpinner];
[outerSpinner startAnimating];
[innerSpinner startAnimating];
}
Result:
The mask property on CALayer uses the alpha component of the mask layer to determine what should be visible and not. Since both of your colors (black and white) are fully opaque (no transparency) the mask has no effect. To fix this you should change one of the colors to a clear color:
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor blackColor] CGColor],
(id)[[UIColor clearColor] CGColor], nil];
EDIT
class Colors {
let colorTop = UIColor(red: 192.0/255.0, green: 38.0/255.0, blue: 42.0/255.0, alpha: 1.0).CGColor
let colorBottom = UIColor(red: 35.0/255.0, green: 2.0/255.0, blue: 2.0/255.0, alpha: 1.0).CGColor
let gl: CAGradientLayer
init() {
gl = CAGradientLayer()
gl.colors = [ colorTop, colorBottom]
gl.locations = [ 0.0, 1.0]
}
}
I drew a line using drawRect. Now I want to change the color of it.
Example Code: self.pattern.backgroundColor = [UIColor clearColor];
I need to be able to change the color of the drawRect like that code. I get stuck on the second part of the code.
self. ? . backgroundColor: The questions marks is where i get stuck. What should I put there for a drawRect?
Here is some code from my drawRect too:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// Setup background
//CGFloat components1[] = {0.0, 0.0, 0.0, 1.0};
//CGColorSpaceRef colorspace1 = CGColorSpaceCreateDeviceRGB();
////CGColorRef black = CGColorCreate(colorspace1, components1);
//CGContextSetFillColorWithColor(context,black);
//CGContextSetRGBFillColor(context,0.0,0.0,0.0,1.0);
// 1 pixel: iphone 6 the point = 0.5 and 6 plus = 0.333333;
//CGFloat point =1.f/[UIScreen mainScreen].scale;
CGFloat point =width/[UIScreen mainScreen].scale;
CGContextSetLineWidth(context,point);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGFloat red [] = {1.0, 0.0, 0.0, 1.0}; // green
CGFloat green [] = {0.0, 1.0, 0.0, 1.0}; // green
CGColorRef color = CGColorCreate(colorspace, green);
CGContextBeginPath(context);
if(bFirstLine && bAlter)
{
CGColorRef colorR = CGColorCreate(colorspace, red);
CGContextSetStrokeColorWithColor(context, colorR);
}
}
[[UIColor redColor] setFill]; // to set the color for fillings
[[UIColor redColor] setStroke]; // to set the color for strokes
and in your case (if i got you right):
[self.backgroundColor setFill];
[self.backgroundColor setStroke];
I want to draw the Circular progress view with gradient as image :
Design Image
I tried the below code but the result color is not correct. This is my code:
//draw gradient
CGFloat colors [] = {
1.0, 0.7,
0.2, 0.4
};
CGFloat locations[] = { 0.0, 1.0 };
CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceGray(); // gray colors want gray color space
CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, colors, locations, 2);
CGColorSpaceRelease(baseSpace), baseSpace = NULL;
CGContextSaveGState(context);
CGContextAddPath(context, progressPath);
CGContextClip(context);
CGRect boundingBox = CGPathGetBoundingBox(progressPath);
CGPoint gradientStart = CGPointMake(0.0, CGRectGetMinY(boundingBox));
CGPoint gradientEnd = CGPointMake(1.0, CGRectGetMaxY(boundingBox));
CGContextDrawLinearGradient(context, gradient, gradientStart, gradientEnd, 0);
CGGradientRelease(gradient), gradient = NULL;
CGContextRestoreGState(context);
The progressPath is the progress line with color :
[UIColor colorWithRed:21/255.0f green:182/255.0f blue:217/255.0f alpha:1.0]
UPDATE:
This is an image my code produced: Result Image
The result image is not the same the design, the color is not the same. I dont know why.
Please help me to correct this. Thanks in advance.
The kind of gradient you are looking for, where the color varies as a function of the angle around a center point, is called an angle gradient. Unfortunately, iOS does not have a built-in way to draw angle gradients, but you can do it using an open-source library such as paiv/AngleGradientLayer.
Working code:
- (void) setGradientWithFrame:(CGRect)frame
{
//create the gradient
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = frame;
//this example is white-black gradient, change it to the color you want..
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor whiteColor] CGColor], (id)[[UIColor blackColor] CGColor], nil];
gradient.cornerRadius = frame.size.height/2.0f;
[self.view.layer insertSublayer:gradient atIndex:0];
CALayer *whiteCircle = [CALayer layer];
whiteCircle.frame = CGRectMake(frame.origin.x + 10, frame.origin.y + 10, frame.size.width - 20, frame.size.height - 20);
whiteCircle.backgroundColor = [[UIColor whiteColor] CGColor];
whiteCircle.cornerRadius = whiteCircle.frame.size.width/2.0f;
[self.view.layer insertSublayer:whiteCircle atIndex:1];
CALayer *whitesquare = [CALayer layer];
whitesquare.frame = CGRectMake(50, frame.size.height - 20, 25, 25);
whitesquare.backgroundColor = [[UIColor whiteColor] CGColor];
whitesquare.transform = CATransform3DMakeRotation(-20.0 / 180.0 * M_PI, 0.0, 0.0, 1.0);
[self.view.layer insertSublayer:whitesquare atIndex:2];
}
to call the method:
[self setGradientWithFrame:CGRectMake(0, 0, 100, 100)];
PS. change the color to the color of gradient to the color you want:
I would like to draw a circular gradient like in the example picture below. I can manage a radial gradient easily but I am not sure how to do a circular one.
I was thinking of drawing a gradient on a line and then transforming it to a circle. Would that be possible?
This is how I draw the radial gradient:
CGFloat a = MIN(self.frame.size.width, self.frame.size.height) - _lineWidth;
//Get current context
CGContextRef mainContext = UIGraphicsGetCurrentContext();
CGRect newRect = rect;
newRect.origin.x = ((self.frame.size.width - a)/2.0f);
newRect.origin.y = ((self.frame.size.height - a)/2.0f);
newRect.size.width = a;
newRect.size.height = a;
// Fill circle
const CGFloat *_components = CGColorGetComponents(_fillColor.CGColor);
CGFloat red = _components[0];
CGFloat green = _components[1];
CGFloat blue = _components[2];
CGFloat alpha = _components[3];
CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
CGFloat redBallColors[] = {
red,green,blue,0.5,
red,green,blue,alpha,
};
CGFloat glossLocations[] = {0.0, 1.0};
CGGradientRef ballGradient = CGGradientCreateWithColorComponents(baseSpace, redBallColors, glossLocations, 2);
CGPoint startPoint = self.center;
CGPoint endPoint = self.center;
CGContextDrawRadialGradient(mainContext, ballGradient, startPoint, 0, endPoint, a/2, 0);
Thanks a lot!