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.
Related
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.
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.
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)];
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.
I have a couple of UITableViews which should fade out at the top. They are placed next to each other in a single UIView. Since all the table views are supposed to fade at the same place, I figured that I should set an alpha mask on the container UIView. This works fine, the scrolling is smooth in all table views and the fade looks nice.
The problem is when the interface orientation changes. It's REALLY choppy. And only when the mask is applied. If I remove the fade, everything is smooth.
This is the code for applying said mask:
// If you want the Table Views to "fade out" at the top, use this function to set the height!
- (void) setFadeHeight:(CGFloat)fadeHeight
{
if (fadeHeight == 0.0) {
self.layer.mask = nil;
return;
}
// Create a gradient layer to use as mask
CAGradientLayer *l = [self createGradientLayerWithHeight:fadeHeight];
l.shouldRasterize = NO;
[self.layer setMask:l];
}
// Creates a transparency gradient. Helper function for the above function
- (CAGradientLayer *) createGradientLayerWithHeight:(CGFloat)gradientHeight
{
CAGradientLayer *mask = [CAGradientLayer layer];
mask.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0],
[NSNumber numberWithFloat:1.0],
nil];
mask.colors = [NSArray arrayWithObjects:
(__bridge id)[UIColor clearColor].CGColor,
(__bridge id)[UIColor whiteColor].CGColor,
nil];
mask.frame = CGRectMake(0.0, 0.0, self.bounds.size.width, self.bounds.size.height);
// vertical direction
mask.startPoint = CGPointMake(0.0, 0.0);
mask.endPoint = CGPointMake(0.0, gradientHeight / self.bounds.size.height);
return mask;
}
The issue might be related to this Stack Overflow question, but since I have smooth scrolling in my tables I'm not so sure. I've also tried setting shouldRasterize to NO according to the answers in that question.