Encapsulate drawRect gradients in CAGradientLayer - ios

There are dozens of excellent examples on here and elsewhere of how to use gradients by
-(void)drawRect:(CGRect)rect
However, I'm new to Quartz and it's likely I'm missing something. When I found this, it seems more intelligent to create a CAGradientLayer and then add it to my view with something like:
-(void)viewWillAppear
{
CAGradientLayer *bgLayer = [BackgroundLayer blueGradient];
bgLayer.frame = self.view.bounds;
[self.view.layer insertSublayer:bgLayer atIndex:0];
}
Where the blueGradient is a separate class method (within BackgroundLayer.m) which creates the gradient, as follows:
+ (CAGradientLayer*) blueGradient {
UIColor *colorOne = [UIColor colorWithRed:(120/255.0) green:(135/255.0) blue:(150/255.0) alpha:1.0];
UIColor *colorTwo = [UIColor colorWithRed:(57/255.0) green:(79/255.0) blue:(96/255.0) alpha:1.0];
NSArray *colors = [NSArray arrayWithObjects:(id)colorOne.CGColor, colorTwo.CGColor, nil];
NSNumber *stopOne = [NSNumber numberWithFloat:0.0];
NSNumber *stopTwo = [NSNumber numberWithFloat:1.0];
NSArray *locations = [NSArray arrayWithObjects:stopOne, stopTwo, nil];
CAGradientLayer *headerLayer = [CAGradientLayer layer];
headerLayer.colors = colors;
headerLayer.locations = locations;
return headerLayer;
}
So here's my question. What if instead of this simple linear gradient, I want to create a radial one? How can I extend blueGradient so that it can handle two dimensional gradients? Or what if I want to add gradients on top of gradients? the drawRect function seems so limiting.
Or maybe that's just the wrong approach... then what's the wiring that I'm missing? How do I add gradients to my view(s) using drawRect? I want to be sure I'm doing it in a modular way so I can add gradient overlays, etc, as additional layers as necessary.

As of iOS 7, CAGradientLayer can only draw a linear gradient. It cannot draw a radial gradient.
You can either use the drawRect: approach, or you can draw your gradient into an image and display the image in a view or layer. You can draw it into a UIImage and display it in a UIImageView, or you can draw it into a CGImage and set it as the contents of a CALayer.

Related

How to set the gradient angle and type to the CAGradientLayer

Hi i am trying to get the color from the gradients where that color is set to another view .
I am able to set start and end color,but not able to set the angle and the type.
Here are the values:
1.Startcolor:#"2b2b2b"
2.Endcolor:#"4a4a4a"
3.GradientAngle:90
4.GradientType:#"linear"
UIView *theView=[[UIView alloc] init];
theView.frame=self.view.frame;
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = theView.bounds;
UIColor *startColor = [self colorwithHexString:#"2b2b2b" alpha:1];
UIColor *endColor = [self colorwithHexString:#"4a4a4a" alpha:1];
gradient.colors = [NSArray arrayWithObjects:(id)[startColor CGColor], (id)[endColor CGColor], nil];
[theView.layer insertSublayer:gradient atIndex:0];
CAGradientLayer has a type property (but the only supported value is axial):
An axial gradient (also called a linear gradient) varies along an axis between two defined end points. All points that lie on a line perpendicular to the axis have the same color value.
The angle is determined by the startPoint and endPoint properties. Both are defined in the unit coordinate space of the layer's bounds (x and y range from 0 to 1).

Set a gradient Background to ImageView, rotation

I want to set a gradient Background to my ImageView. I found a solution for that here on Stack Overflow. But it doesn't really satisfy my needs.
I want the gradient to be from left to right. With my the code I found I am only able to fill it from top to bottom.
I create the gradient layer with this code:
+ (CAGradientLayer *)colorGradientWithColor:(UIColor *)color
{
UIColor *colorOne = color;
UIColor *colorTwo = [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:0.0f];
NSArray *colors = [NSArray arrayWithObjects:(id)colorOne.CGColor, colorTwo.CGColor, nil];
NSNumber *stopOne = [NSNumber numberWithFloat:0.0];
NSNumber *stopTwo = [NSNumber numberWithFloat:0.9];
NSArray *locations = [NSArray arrayWithObjects:stopOne, stopTwo, nil];
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.colors = colors;
gradientLayer.locations = locations;
return gradientLayer;
}
Then I add the layer to my UIImageView:
CAGradientLayer *gradientLayer = [BackgroundGradient colorGradientWithColor:difficultyColor];
gradientLayer.frame = myImageView.bounds;
[myImageView.layer insertSublayer:gradientLayer atIndex:0];
And here is the result:
And here my interface setup:
As you can see, there a two UIImageViews. I want to rotate the layer in the gradient view, so it goes from left to right and not how it is now.
With the following code fragment I am able to rotate the whole ImageView. This only kind of solves my problem, but I don't want that, because it kind of destroys my interface..
myImageView.transform = CGAffineTransformMakeRotation(M_PI*1.5f);
So basically I am searching for a solution to do the gradient from left to right instead from the top to the bottom.
Do you guys have any ideas? I am new to XCode and would appreciate every tip!
This is the result I got from below code. I guess this is what you are trying to achieve
- (CAGradientLayer *)colorGradientWithColor:(UIColor *)color
{
UIColor *colorOne = color;
UIColor *colorTwo = [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:0.0f];
NSArray *colors = [NSArray arrayWithObjects:(id)colorOne.CGColor, colorTwo.CGColor, nil];
NSNumber *stopOne = [NSNumber numberWithFloat:0.0];
NSNumber *stopTwo = [NSNumber numberWithFloat:0.9];
NSArray *locations = [NSArray arrayWithObjects:stopOne, stopTwo, nil];
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
[gradientLayer setStartPoint:CGPointMake(0.0, 0.5)];//add these lines
[gradientLayer setEndPoint:CGPointMake(1.0, 0.5)];
gradientLayer.colors = colors;
gradientLayer.locations = locations;
return gradientLayer;
}
Try to rotate your layer before adding it to imageview,using transform.
Use startpoint and endpoint for the gradient layer:
BOOL horizontal = YES;
//(YES for left to right or NO for top to bottom)
gradientLayer.startPoint = horizontal ? CGPointMake(0, 0.5) : CGPointMake(0.5, 0);
gradientLayer.endPoint = horizontal ? CGPointMake(1, 0.5) : CGPointMake(0.5, 1);

Gradient text in a UITextField

I'm using a CAGradientLayer to make a gradient background for a text field. It looks like this
Instead of the background I want to have the gradient on the text. How can I make gradient text in a UITextField?
The code I used to make the gradient in the image
pinkDarkOp = [UIColor colorWithRed:0.9f green:0.53f blue:0.69f alpha:1.0];
pinkLightOp = [UIColor colorWithRed:0.79f green:0.45f blue:0.57f alpha:1.0];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = [[myTextField layer] bounds];
gradient.cornerRadius = 7;
gradient.colors = [NSArray arrayWithObjects:
(id)pinkDarkOp.CGColor,
(id)pinkLightOp.CGColor,
nil];
[[myTextField layer]addSublayer:gradient];
You can solve this by creating two layers:
First you create a layer which you fill with the gradient.
Then you create another layer containing the text.
Finally you use the second layer as a mask for masking the first layer. This is achieved by assigning the second layer to the mask property of the first layer.
If I look more specifically on your code example, I think that everything before the last line should be OK. But then you should replace the last line with something like this:
gradient.mask = myTextField.layer;
Perhaps you need to do some adjustments to myTextField as well (hard to say since its definition is not part of the listed code). It's important to understand that the text in the mask must be opaque but the rest of that layer must be fully transparent. Read about the "mask" property in the CALayer class reference:
https://developer.apple.com/library/ios/DOCUMENTATION/GraphicsImaging/Reference/CALayer_class/Introduction/Introduction.html#//apple_ref/occ/instp/CALayer/mask
Dislaimer: I have not had time to test the code. I leave that to you. But the general idea of masking one layer with another should be possible to use, so I am convinced that you can sort out the details by yourself.
i made somme changes in my codes
pinkDarkOp = [UIColor colorWithRed:0.9f green:0.53f blue:0.69f alpha:1.0];
pinkLightOp = [UIColor colorWithRed:0.79f green:0.45f blue:0.57f alpha:1.0];
gradient.frame = [[myTextField layer] bounds];
gradient.cornerRadius = 7;
gradient.colors = [NSArray arrayWithObjects:(id)pinkDarkOp.CGColor,
(id)[[UIColor clearColor] CGColor], nil];
gradient.mask=myTextField.layer;
the text is invisible now

CAGradientLayer sometimes not rendering

I have a CAGradientLayer drawing in the background on viewDidLoad. It works some of the time, but then sometimes it just doesn't render anything persistently until I restart my computer. I can't figure out why some of the time it would work and then other times it won't. It will work lets say 5 builds in a row and then it will just stop rendering. No errors. Anyone have experience with this?
Background Layer Method:
+ (CAGradientLayer*) morningGradient {
UIColor *mornTop = [UIColor colorWithRed:0.843 green:0.722 blue:0.667 alpha:1.000];
UIColor *mornBottom = [UIColor colorWithRed:0.584 green:0.733 blue:0.945 alpha:1.000];
NSArray *colors = [NSArray arrayWithObjects:(id)mornTop.CGColor, mornBottom.CGColor, nil];
NSNumber *stopOne = [NSNumber numberWithFloat:0.0];
NSNumber *stopTwo = [NSNumber numberWithFloat:0.7];
NSNumber *stopThree = [NSNumber numberWithFloat:1.0];
NSArray *locations = [NSArray arrayWithObjects:stopOne, stopTwo, stopThree, nil];
CAGradientLayer *headerLayer = [CAGradientLayer layer];
headerLayer.colors = colors;
headerLayer.locations = locations;
return headerLayer;
}
Draw Method:
-(void)drawGrad
{
NSLog(#"drawing gradient");
CAGradientLayer *bgLayer = [BackgroundLayer morningGradient];
bgLayer.frame = self.view.bounds;
[self.view.layer insertSublayer:bgLayer atIndex:0];
}
ViewDidLoad:
- (void)viewDidLoad
{
[self drawGrad];
[super viewDidLoad];
}
From the documentation of CGGradientCreateWithColors(colorSpace, colors, locations[]) you can read
The locations array should contain the same number of items as the colors array.
I'm assuming that the same is true for CAGradientLayer but can't find anything in the documentation. I'm making this assumption because it makes sense. How would you really interpret two colors and three locations? What should be the color at the third location?
Change your code so that you pass the same number of colors as locations.
create a custom gradient view with round rect of size 3, you need to add the QuartzCore framework and then follow the code below (.h file and .m file)
#import <UIKit/UIKit.h>
#interface CustomGradientView : UIView
#end
#import "CustomGradientView.h"
#import <QuartzCore/QuartzCore.h>
#implementation CustomGradientView
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
UIColor *color1=[UIColor whiteColor];
CGColorRef startColor =color1.CGColor;
UIColor *color2=[UIColor redColor];
CGColorRef endColor = color2.CGColor;
drawLinearGradient(context, rect, startColor, endColor);
CGPathRef p = [[UIBezierPath bezierPathWithRoundedRect:rect
cornerRadius:3] CGPath];
CGContextAddRect(context, rect);
CGContextAddPath(context, p);
CGContextEOClip(context);
CGContextClearRect(context, rect);
}
#end

CAGradientLayer covers everything drawn in drawRect

I have the following code:
-(void)drawRect:(CGRect)rect
{
// gradient background
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = rect;
gradient.colors = [NSArray arrayWithObjects:(id) backgroundGradientTop.CGColor, (id) backgroundGradientBottom.CGColor, nil];
gradient.locations = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0f], [NSNumber numberWithFloat:0.7], nil];
[self.layer insertSublayer:gradient atIndex:0];
// line on top
[[UIColor redColor] set];
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(currentContext, 5.0f);
CGContextMoveToPoint(currentContext, 0, 10.0f);
CGContextAddLineToPoint(currentContext, rect.size.width, 10.0f);
CGContextStrokePath(currentContext);
}
the line i'm trying to draw on top of the gradient is never shown. If i comment out the gradient layer it is there. Is there someway to draw both a gradient background and a line (or a few lines) on top? Maybe i shouldn't be mixing calayer and CG?
The line i'm trying to draw on top of the gradient is never shown. If i comment out the gradient layer it is there.
That's because sublayers appear on top of their parent layers. Your gradient is apparently opaque, and the same size as your view, so it covers up the view.
You can't mix CA and CG drawing this way. It would work better if you drew the gradient using CGContextDrawLinearGradient.

Resources