CAGradientLayer cannot change color - ios

I want a colored gradient to overlay my view. In a view controller, I have this code
- (void)viewDidLoad {
self.view.backgroundColor = [UIColor whiteColor];
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = self.view.bounds;
gradientLayer.colors = [NSArray arrayWithObjects:(id)[UIColor redColor].CGColor, (id)[UIColor clearColor].CGColor, nil];
gradientLayer.startPoint = CGPointMake(0, 0);
gradientLayer.endPoint = CGPointMake(0, 1.0f);
self.view.layer.mask = gradientLayer;
}
But even though the first color is red, I only ever see a black gradient. How can I display a red gradient instead?

TLDR: Instead of setting the gradient as the layer mask, add the gradient layer as a sublayer of view.layer.
Layers use the layer mask mask to determine the alpha of their own content by using the alpha of the mask at each pixel, since your gradientLayer is fully opaque, the effect you were getting wasn't the one you were hoping for.
Layers are similar to views (views are actually wrappers for layers), you can add them as sublayers in a similar way that views are added as subviews.

Related

CAGradientLayer not draw the gradient correctly

I use CAShapeLayer to draw a circle and set it as the mask of CAGradientLayer, here is the code:
CAShapeLayer *circleShapeLayer = [CAShapelayer new];
// draw circle path code here...
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.colors = #[(__bridge id)[UIColor whiteColor].CGColor,
(__bridge id)[UIColor clearColor].CGColor];
// self here means a UIView
gradientLayer.mask = circleShapeLayer;
gradientLayer.frame = self.bounds;
[self.layer addSublayer:gradientLayer];
When I run the app, it will display a gradient circle, but the gradient is strange.
What I want is a circle that at start point, the color is white and at end point the color is clear color, it should look like this:
But the color of the circle in the Simulator screen is:
The color is symmetric.
I think the problem is that I do not set the gradientLayer.colors correctly, how can I fix it?
CAGradientLayer can not paint gradient along an arc. on the other hand, the mask layer's frame is too small than gradient layer's frame to see clear color
To Umair's response, it didn't make sense to me at first because even whiteColor or blackColor is being fetched by colorWithRed:green:blue:alpha but then I read this in the documentation:
When rendered, the colors are mapped to the output color space before being interpolated.
So maybe this is really it:
gradientLayer.colors = #[(__bridge id)[UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f].CGColor,
(__bridge id)[UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:0.0f].CGColor];
#J.Hunter's answer is correct in that CAGradientLayers cannot draw along an arc. This means that your drawn gradients will be limited to radial and linear. You are specifically looking to create an angled gradient, which I've attempted to do in the past as well.
Unfortunately, CAGradientLayer is bound to these limitations, and the best way I've found to create a masked angled gradient is to mask with a UIImage that contains an angled gradient. This won't be nearly as dynamic as drawing your own gradient, but it seems to be the best (possibly only) option at the moment.

How I can make a gradient with three opposite points?

I want to make a color picker just like in the picture, but I could not do the triangle, which has three points to show the gradient, White, Black and the selected color.
The black and white are always the same, but the color selected should vary according to the user selects in the circle, but do not know how to make a gradient opposite to those three points, and displays the image.
(Only objective c, please)
Hope, someone could help me. Thanks.
HSL Picker
Here is what you can try to create gradient with three colors:
Get the reference from your triangle edges that have the reference to the color they point to.
BOOL isHorizontal=YES;
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = self.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[self.startColor CGColor],(id)[self.midColor CGColor], (id)[self.endColor CGColor], nil];
gradient.endPoint = (self.isHorizontal) ? CGPointMake(1, 0) : CGPointMake(0, 1);
[self.layer insertSublayer:gradient atIndex:0];
If you want to change the gradient direction, play around with:
[gradient setStartPoint:CGPointMake(0.0, 0.5)];
[gradient setEndPoint:CGPointMake(1.0, 0.5)];

Animate the mask of a mask of a CALayer

Goal:
I am drawing a custom shape that has a gradient. I also want to animate the drawing of this shape by having it draw in from left to right.
Problem:
The code below works on an iPad simulator, but it doesn't work on my iPad 4 running iOS 7. Does anyone know how to make this work on the device? Is there a different way to achieve this same result?
Explanation of my Code:
My code works (only on simulator) using 3 CALayers.
gradientLayer holds my gradient.
shapeMaskLayer holds my custom shape.
animationMaskLayer animates it's path to simulate drawing from left to right
animationMaskLayer --masks--> shapeMaskLayer --masks--> gradientLayer
I then animate the frame of animationMaskLayer to animate the whole shape.
Code:
// Animation Mask Rects
CGPathRef leftStartingRectPath = CGPathCreateWithRect(CGRectMake(0, 0, 0, self.frame.size.height), 0);
CGPathRef fullViewRectPath = CGPathCreateWithRect(CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), 0);
// Animation Mask Layer
CAShapeLayer *animationMaskLayer = [CAShapeLayer layer];
animationMaskLayer.path = fullViewRectPath;
animationMaskLayer.fillColor = UIColor.blackColor.CGColor;
// Shape Mask Layer
CAShapeLayer *shapeMaskLayer = [CAShapeLayer layer];
shapeMaskLayer.path = CGPathCreateWithEllipseInRect(self.bounds, 0);
shapeMaskLayer.fillColor = [UIColor blueColor].CGColor;
shapeMaskLayer.mask = animationMaskLayer;
// Gradient Layer
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = self.bounds;
gradientLayer.colors = self.colors;
gradientLayer.mask = shapeMaskLayer;
mountainLayer.anchorPoint = pt(0, 0);
mountainLayer.position = pt(0, 0);
[self.layer addSublayer:gradientLayer];
// Left To Right Animation
CABasicAnimation *leftToRightAnimation = [CABasicAnimation animationWithKeyPath:#"path"];
leftToRightAnimation.duration = 0.5;
leftToRightAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
leftToRightAnimation.fromValue = (__bridge id)leftStartingRectPath;
leftToRightAnimation.toValue = (__bridge id)fullViewRectPath;
leftToRightAnimation.fillMode = kCAFillModeForwards;
leftToRightAnimation.removedOnCompletion = NO;
[animationMaskLayer addAnimation:leftToRightAnimation forKey:#"animatePath"];
// Memory Management
CGPathRelease(leftStartingRectPath);
CGPathRelease(fullViewRectPath);
Animating the mask of a mask? I'm surprised that even works in the simulator.
Have you tried nesting two layers, with the parent layer with masksToBounds on? You can then set an independent mask on both layers, and the outer layer will effectively mask your inner layer with its own mask (due to masksToBounds). It probably doesn't matter which layer gets which mask (because you want the intersection of both masks).
With the code you listed in your question, you would just need to add one line, and comment out one line to get the correct functionality:
self.layer.mask = animationMaskLayer;
// shapeMaskLayer.mask = animationMaskLayer;

UIView Gradient using CAGradientLayer always blue

I am trying to create a UIView with a dark gray gradient:
UIView *sectionSpacer = [[UIView alloc] init];
sectionSpacer.backgroundColor = [UIColor clearColor];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = sectionSpacer.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[RGB(80, 83, 88) CGColor], (id)[RGB(69, 71, 73) CGColor], nil];
[sectionSpacer.layer addSublayer:gradient];
For some reason the gradient view is always blue even thought the RGB values are dark grays. Why is this?
If you just init the view without a frame, it'll default to CGRectZero, so your layer will also have 0 width and height, which is why you don't see it (the gradient you do see looks like a standard UITableView header that may come from elsewhere, I doubt that it is even the same view you're initializing in the code you've shown).

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