CAGradientLayer behind self.layer appearing above - objective-c

I'm trying to make a custom UIButton class, except, when drawing the background of the button, and adding it as a sublayer using insertSubLayer behind: method, it still appears infront of the UIButton Textlabel.
My code is posted below, Any help would be greatly appreciated.
CALayer *layer = self.layer;
layer.cornerRadius = 3.0f;
layer.masksToBounds = YES;
layer.borderWidth = 1.0f;
layer.borderColor = [UIColor colorWithWhite:0.5f alpha:0.5f].CGColor;
self.titleLabel.textColor = [UIColor greenColor];
//layer.backgroundColor = [UIColor greenColor].CGColor;
bgColor = [CAGradientLayer layer];
bgColor.frame = self.layer.bounds;
self.backgroundColor = [UIColor colorWithWhite:1 alpha:1];
bgColor.colors = [NSArray arrayWithObjects:
(id)[UIColor colorWithWhite:0.97f alpha:1].CGColor,
(id)[UIColor colorWithWhite:0.87f alpha:1].CGColor,
nil];
bgColor.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:1],
nil];
[self.layer addSublayer:bgColor];
[self.layer insertSublayer:bgColor below:layer];

self.layer and layer in your code point to the same object. You're asking the layer to insert a sublayer behind itself - this is not possible. Sublayers are contained within the parent layer. Try
[self.layer insertSublayer:bgColor atIndex:0];
Instead of
[self.layer addSublayer:bgColor];
[self.layer insertSublayer:bgColor below:layer];
This will add the gradient at the lowest possible point in the layer hierarchy of your button.

Related

Making a gradient arc?

i wanna make an arc with the use of CAGradientLayer in such a way:-
20% of the arc have same single color then other 80% of arc has a gradient of two colors.
I already tried by hand on locations
startPoint endPoint property of CAGradientLayer but couldn't get the success and already go through from tutorials but somehow couldn't understand the concept properly.please solve my problem with clear description of it.
// for beizier path
- (UIBezierPath *)samplePath
{
UIBezierPath *path = [UIBezierPath bezierPath];
path = [UIBezierPath bezierPath];
[path addArcWithCenter:CGPointMake(200, 200) radius:30.0f startAngle:(3*M_PI)/4 endAngle:M_PI/4 clockwise:YES];
path.lineWidth = 30;
[[UIColor redColor] setStroke];
// [[UIColor colorWithRed:arc4random() green:arc4random() blue:arc4random() alpha:1.0] setFill];
[path stroke];
return path;
}
- (void)startAnimation
{
CAShapeLayer *shapeLayer;
if (self.pathLayer == nil)
{
shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [[self samplePath] CGPath];
shapeLayer.strokeColor = [[UIColor redColor] CGColor];
shapeLayer.fillColor = nil;
shapeLayer.lineWidth = 30;
self.pathLayer = shapeLayer;
}
[self animationBasic];
[self gradientLayer:shapeLayer];
}
-(void)animationBasic
{
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 3.0;
pathAnimation.fromValue = #(0.0f);
pathAnimation.toValue = #(1.0f);
[self.pathLayer addAnimation:pathAnimation forKey:#"strokeEnd"];
}
-(void)gradientLayer:(CAShapeLayer *)shapelayer
{
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = self.frame;
gradientLayer.colors = #[(__bridge id)[UIColor yellowColor].CGColor,(__bridge id)[UIColor redColor].CGColor ];
gradientLayer.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.5f],
[NSNumber numberWithFloat:1.0f],
nil];
// gradientLayer.startPoint = CGPointMake(0,1);
// gradientLayer.endPoint = CGPointMake(1,1);
[self.layer addSublayer:gradientLayer];
gradientLayer.mask = shapelayer;
}
The locations property of a CAGradientLayer instance should be populated with the starting points (I know, the word stops makes it sound the opposite) of each color as specified in the colors property, from 0 to 1. These stops are rendered vertically from top to bottom.
So, this should work:
gradientLayer.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:0.5f],
nil];
Now, at the same time, if you want to make the gradient horizontal (or any other direction), you can make use of the startPoint and endPoint properties.
gradientLayer.startPoint = CGPointMake(0.0, 0.5); //Default is (0.5, 0,0)
gradientLayer.endPoint = CGPointMake(1.0, 0.5); //Default is (0.5, 1.0)
//The gradientLayer will be rendered horizontally.
The startingPoint and endingPoint properties determine the direction and layout of the gradient in the layer. These are defined in the unit coordinate system, where each point is defined as a set of unit coordinates and the top left corner being the origin. So, (0,0) corresponds to the origin, (1,1) corresponds to the bottom right corner. You can check this excellent answer for a better explanation.
So to summarize, the gradient is rendered in the direction and region as specified by the startPoint and endPoint properties, with the colors rendered in the ratio specified in the locations property.
At the end, you can only understand this fully by tinkering with the properties and using various combinations to see what effect is rendered.

Objective C - Faded Gradient on left/right sides of UICollectionView

I have a horizontal-scrolling UICollectionView within the main view of a view controller like so (Grey is UIView, Wood is UICollectionView):
I want to add fixed faded gradients on the far left & far right sies of this UICollectionView so that the scrolls appear to vanish as the user scrolls. How would I go about doing this? Does it involve some use of CAGradientLayer? I would be grateful for any help you can give me!
I actually managed to figure this out using one mask layer thanks to this tutorial at cocoanetics. Here's what I did:
#interface ScalesViewController : UIViewController
{
CAGradientLayer *maskLayer;
}
#end
Then in the .m, I placed in the following:
- (void)viewDidAppear:(BOOL)animated
{
[super viewWillAppear: animated];
if (!maskLayer)
{
maskLayer = [CAGradientLayer layer];
CGColorRef outerColor = [[UIColor colorWithWhite:0.0 alpha:1.0] CGColor];
CGColorRef innerColor = [[UIColor colorWithWhite:0.0 alpha:0.0] CGColor];
maskLayer.colors = [NSArray arrayWithObjects:
(__bridge id)outerColor,
(__bridge id)innerColor,
(__bridge id)innerColor,
(__bridge id)outerColor, nil];
maskLayer.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0],
[NSNumber numberWithFloat:0.125],
[NSNumber numberWithFloat:0.875],
[NSNumber numberWithFloat:1.0], nil];
[maskLayer setStartPoint:CGPointMake(0, 0.5)];
[maskLayer setEndPoint:CGPointMake(1, 0.5)];
maskLayer.bounds = self.mainCollectionView.bounds;
maskLayer.anchorPoint = CGPointZero;
[self.mainCollectionView.layer insertSublayer: maskLayer atIndex: 0];
}
}
This creates a nice "fade to black" effect on both sides of my collection view. More colors can be added to the locations & color properties to refine the gradient blend. The start/endpoints determine the direction and location of the gradient.
Try to add two layers of CAGradientLayer on the collection view sublayer:
#import <QuartzCore/QuartzCore.h>
CAGradientLayer *leftShadow = [CAGradientLayer layer];
leftShadow.frame = CGRectMake(0, 0, 100, self.collectionView.frame.size.height);
leftShadow.startPoint = CGPointMake(0, 0.5);
leftShadow.endPoint = CGPointMake(1.0, 0.5);
leftShadow.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithWhite:0.0 alpha:0.4f] CGColor], (id)[[UIColor clearColor] CGColor], nil];
[self.collectionView.layer addSublayer:leftShadow];
CAGradientLayer *rightShadow = [CAGradientLayer layer];
rightShadow.frame = CGRectMake(CGRectGetWidth(self.collectionView.frame)-100.0, 0, 100, self.collectionView.frame.size.height);
rightShadow.startPoint = CGPointMake(1.0, 0.5);
rightShadow.endPoint = CGPointMake(0, 0.5);
rightShadow.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithWhite:0.0 alpha:0.4f] CGColor], (id)[[UIColor clearColor] CGColor], nil];
[self.collectionView.layer addSublayer:rightShadow];

How to set gradient color to the background of UILabel in iPhone

I am using the following code to set the gradient color to the background of label but no effect what i am doing wrong?
Code is here:
[self.teamName setTextColor:[UIColor whiteColor]];
[self.teamName setBackgroundColor:[UIColor clearColor]];
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = self.teamName.bounds;
gradientLayer.colors = [NSArray arrayWithObjects:(id)[[UIColor whiteColor]CGColor], (id)[[UIColor blackColor]CGColor], nil];
[self.teamName.layer insertSublayer:gradientLayer atIndex:0];
It working fine when i insert the CAGradientLayer to the main view.
[self.teamName setTextColor:[UIColor whiteColor]];
[self.teamName setBackgroundColor:[UIColor clearColor]];
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = self.teamName.bounds;
gradientLayer.colors = [NSArray arrayWithObjects:(id)[[UIColor whiteColor]CGColor], (id)[[UIColor blackColor]CGColor], nil];
[self.view.layer insertSublayer:gradientLayer atIndex:0];
So I was attempting to do this also! I finally made it work by just placing a UIView behind the label and changing the label background color to clear. I did this in a storyboard.

adding gradient to UIView in drawRect

I am trying to add a CAGradientLayer in my drawRect, the code is the following:
- (void)drawRect:(CGRect)rect {
CAGradientLayer *gradientOverlay = [CAGradientLayer layer];
CGColorRef grayColor = [UIColor colorWithRed:37/255.f green:37/255.f
blue:37/255.f alpha:1.0].CGColor;
CGColorRef blueColor = [UIColor colorWithRed:23.0/255.0 green:171.0/255.0
blue:219.0/255.0 alpha:1.0].CGColor;
gradientOverlay.colors = [NSArray arrayWithObjects:
(id) grayColor,
(id) grayColor,
(id) blueColor,
nil];
gradientOverlay.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0],
[NSNumber numberWithFloat:0.4],
[NSNumber numberWithFloat:1],
nil];
CGPoint startPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMidY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMaxX(rect), CGRectGetMidY(rect));
gradientOverlay.startPoint = startPoint;
gradientOverlay.frame = self.bounds;
gradientOverlay.endPoint = endPoint;
self.layer.mask = gradientOverlay;
}
Any idea why this is not working?
How about trying this instead of overriding drawRect?
+ (void) applyGradient:(NSArray *)cgColors toView:(UIView *)view {
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view.bounds;
gradient.colors = cgColors;
for(CALayer *layer in view.layer.sublayers) {
if([layer isKindOfClass:[CAGradientLayer class]]) return;
}
[view.layer insertSublayer:gradient atIndex:0];
}
Add your CAGradientLayer as a sublayer of your self.layer.
Note: If you're doing it in the drawRect: method make sure you're not adding a new CAGradientLayer every time drawRect: is called.
UPDATE (regarding the comment):
Here's the code for what you're asking:
//Create a layer that holds your background image and add it as sublayer of your self.layer
CALayer *layer = [CALayer layer];
layer.frame = self.layer.frame;
layer.contents = (id)[UIImage imageNamed:#"background.png"].CGImage;
[self.layer addSublayer:layer];
//Create your CAGradientLayer
CAGradientLayer *gradientOverlay = [CAGradientLayer layer];
CGColorRef grayColor = [UIColor colorWithRed:37/255.f green:37/255.f
blue:37/255.f alpha:1.0].CGColor;
CGColorRef blueColor = [UIColor colorWithRed:23.0/255.0 green:171.0/255.0
blue:219.0/255.0 alpha:1.0].CGColor;
gradientOverlay.colors = [NSArray arrayWithObjects:
(id) grayColor,
(id) grayColor,
(id) blueColor,
nil];
gradientOverlay.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0],
[NSNumber numberWithFloat:0.4],
[NSNumber numberWithFloat:1],
nil];
CGPoint startPoint = CGPointMake(CGRectGetMinX(rect), CGRectGetMidY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMaxX(rect), CGRectGetMidY(rect));
gradientOverlay.startPoint = startPoint;
gradientOverlay.frame = self.layer.frame;
gradientOverlay.endPoint = endPoint;
//set its opacity from 0 ~ 1
gradientOverlay.opacity = 0.6f;
//add it as sublayer of self.layer (it will be over the layer with the background image
[self.layer addSublayer:gradientOverlay];
Note: You don't have to do this in drawRect: method. Create your layer hierarchy in the init method for example, if you need to change the geometry of some views/layers override the layoutSubviews method.
Generally, you should only override drawRect: when you will be drawing your view using CoreGraphics.
A better place to put your code would be in init if you're creating this view in code, or awakeFromNib if you're placing this view in a .xib

UIView CAGradient with rounded corners?

I have an UIView with rounded corners and drop shadow, implemented and working. But the UIView has a boring background color, white. So I want to put a gradient layer as the background. Below the labels, buttons and most important, make it so the rounded corners still appears.
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = subHudView.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor blackColor] CGColor], (id)[[UIColor whiteColor] CGColor], nil];
[subHudView.layer addSublayer:gradient];
subHudView.layer.cornerRadius = 8;
subHudView.layer.masksToBounds = NO;
subHudView.layer.shadowOffset = CGSizeMake(-5, 5);
subHudView.layer.shadowRadius = 8;
subHudView.layer.shadowOpacity = 0.75;
This is my code as I tried to implement it, but the gradient layer is on top of everything in the view know. How can I make the gradient go under all the controls and labels? Every responding help will be appreciated.
in Swift:
let gradientLayer = CAGradientLayer()
gradientLayer.cornerRadius = 20.0
A layer added to your view (addSublayer) is drawn in front of your view's own layer, which is where all your view's drawing takes place. What you want is to draw the gradient into your view's own layer. To do so, implement +layerClass in your view, specifying that you want your view's layer to be a gradient view.
So, for example (in the view's own code):
+(Class)layerClass {
return [CAGradientLayer class];
}
-(void)awakeFromNib {
[super awakeFromNib];
CAGradientLayer* layer = (CAGradientLayer*)self.layer;
layer.colors = [NSArray arrayWithObjects:(id)[[UIColor blackColor] CGColor], (id)[[UIColor whiteColor] CGColor], nil];
layer.cornerRadius = 8;
layer.masksToBounds = NO;
layer.shadowOffset = CGSizeMake(-5, 5);
layer.shadowRadius = 8;
layer.shadowOpacity = 0.75;
}
If you then implement drawRect:, however, you'll of course wipe out your gradient and your rounded corners. There are ways around that...
When you addSublayer: it add the layer at the top of all the sublayers.
You should probably use something like that instead :
[subHudView.layer insertSublayer:gradient atIndex:0];
That way, the CAGradientLayer will be below everything else.
As well as the insertSublayer:atIndex: that gcamp suggested, you can also use insertSublayer:above: and insertSublayer:below: to place your layer in specific places.
[self.view.layer insertSublayer:gradient below:someUIObject];
Rather than create a new layer, override your view's layer method:
+ (Class)layerClass
{
return [CAGradientLayer class];
}
This will ensure that your view create a CAGradientLayer, rather than a basic CALayer, as the base layer.
Then, during init get hold of the layer:
CAGradientLayer *gradLayer = self.layer;
gradLayer.colors = [NSArray arrayWithObjects:(id)[[UIColor blackColor] CGColor], (id)[[UIColor whiteColor] CGColor], nil];
and assign your gradients to it.
Nice and clean...