Animating CAGradientLayer - iphone

I'm using...
NSArray *colors = [NSArray arrayWithObjects:(id) colorOne.CGColor, colorTwo.CGColor, nil];
CAGradientLayer *headerLayer = [CAGradientLayer layer];
headerLayer.colors = colors;
headerLayer.frame = self.button_editEntry.bounds;
[headerLayer setCornerRadius:10];
[self.button_editEntry.layer insertSublayer:headerLayer
atIndex:0];
... to get a linear fill happening on my button. The problem is when I animate the frame size (using UIView beginAnimations) the CAGradientLayer disappears and doesn't animate with the rest of the frame. Is there a reason this isn't working?
Is there a better way to do linear fades?

Related

CAGradientLayer SetMask on CAShapeLayer crashes app

Using example code from many great people, I am able to make a complex shape using CAShapeLayer and present that no problem using a subclassed UIView:
#implementation BMTestLogo
+ (Class)layerClass {
return [CAShapeLayer class];
}
- (void)layoutSubviews {
[self setUpPaths];
[self setLayerProperties];
// [self attachAnimations];
}
- (void)setLayerProperties {
CAShapeLayer *layer = (CAShapeLayer *)self.layer;
// _gradient = [CAGradientLayer layer];
CGMutablePathRef combinedPath = CGPathCreateMutableCopy([[_UIBezierPathsArray objectAtIndex:0] CGPath]);
for (UIBezierPath* path in _UIBezierPathsArray) {
CGPathAddPath(combinedPath, NULL, path.CGPath);
}
layer.path = combinedPath;
layer.fillColor = [UIColor clearColor].CGColor;
layer.fillRule = kCAFillRuleEvenOdd;
}
This code works great, now trying to add a CAGradientLayer and mask over the shape, I keep getting a crash, specifically:
QuartzCore CA::Layer::ensure_transaction_recursively(CA::Transaction*):
Here is the Gradient Code, note, this is taken from other, working examples on SO:
#implementation BMViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[_testLogo setNeedsDisplay];
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.startPoint = CGPointMake(0.5,1.0);
gradientLayer.endPoint = CGPointMake(0.5,0.0);
gradientLayer.frame = CGRectMake(CGRectGetMinX(_testLogo.layer.bounds), CGRectGetMinY(_testLogo.layer.bounds), CGRectGetWidth(_testLogo.layer.bounds), CGRectGetHeight(_testLogo.layer.bounds));
NSMutableArray *colors = [NSMutableArray array];
for (int i = 0; i < 2; i++) {
[colors addObject:(__bridge id)[UIColor colorWithHue:(0.1 * i) saturation:1 brightness:.8 alpha:1].CGColor];
}
gradientLayer.colors = colors;
NSNumber *stopOne = [NSNumber numberWithFloat:0.0];
NSNumber *stopTwo = [NSNumber numberWithFloat:1.0];
NSArray *locations = [NSArray arrayWithObjects:stopOne, stopTwo, nil];
gradientLayer.locations = locations;
gradientLayer.needsDisplayOnBoundsChange = YES;
[gradientLayer setMask:_testLogo.layer];
[_testLogo.layer insertSublayer:gradientLayer atIndex:0];
// Do any additional setup after loading the view, typically from a nib.
}
I have tried to debug and research this issue, and the usual suspects of the UIColors not being CGColors or (__bridge id) properly are not there, I am using two locations for the gradient, with only 2 colors, and moreover I have tried dozens of different versions: with or without setNeeds display, properties vs. local declarations, the gradient layer inside the sub class and out, all still giving me the same crash when inserting thesublayer.
When I take out the setMask: call, it does not crash, and just fills the whole frame with the gradient.
This seems like I'm just missing something minor or obvious. Note, using iOS 6 and ARC -- would like to keep using CAShapeLayer and CAGradientLayer as it makes it a bit easier to animate (which is the end goal).
The following code is triggering a stack overflow in ensure_transaction_recursively.
[gradientLayer setMask:_testLogo.layer];
[_testLogo.layer insertSublayer:gradientLayer atIndex:0];
The shape layer has a gradient layer subview which has a shape layer mask which has a gradient layer subview...
Since your goal is to have a gradient layer that is masked by a shape, BMTestLogo should be backed by the CAGradientLayer which then has a the CAShapeLayer as its mask. The shape layer should never reference the gradient layer.

Adding Gradient Hides UILabel Text In UITableViewCell

I am trying to add gradient layer behind the UILabel's text which resides in a custom UITableViewCell. Problem is that gradient is hiding my label's text. I have already visited this link, but it is not working for me.So how can I add it behind the text layer?
What I have done so far is:
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = CGRectMake(0, 0, buyPrice_port.frame.size.width, buyPrice_port.frame.size.height);
gradient.colors = [NSArray arrayWithObjects:(id)gainBackgroundColor1, (id)gainBackgroundColor2, nil];
gradient.locations = [NSArray arrayWithObjects: [NSNumber numberWithFloat:0.00], [NSNumber numberWithFloat:0.70] , nil];
[buyPrice_port.layer insertSublayer:gradient atIndex:0];
buyPrice_port.textColor = [UIColor blackColor];
You can simply add a view behind the UILabel (i.e. add the label as a subview of the gradient view) and keep the label transparent.

UIView with glass effect

I want to create a view which should have a glass like effect. It should look shining as well.
On that i want to add a UITextView which should appear transparent. I'm new to IPhone and not getting how to do this.
I don't want to add image. Wanna do programmatically. The view should look like as if it is a mobile screen.
Thanks
You'll have to do things if you don't want to use the image.
Add one UIView Use this code. And set gradient background color to it. Which will give you shiny glass like effect.
Here is the code:
.h file :
UIColor *pinkDarkOp;
UIColor *pinkLightOp;
CAGradientLayer *gradient;
.m file :
img_TopBarView = [[UIView alloc]initWithFrame:CGRectMake(0.0,0.0,1024.0,50.0)];
img_TopBarView.userInteractionEnabled = YES;
pinkDarkOp = [UIColor colorWithRed:15.0f/255.0 green:138.0f/255.0 blue:216.0f/255.0 alpha:1.0];
pinkLightOp = [UIColor colorWithRed:12.0f/255.0 green:91.0f/255.0 blue:183.0f/255.0 alpha:1.0];
gradient = [CAGradientLayer layer];
gradient.frame = [[img_TopBarView layer] bounds];
gradient.colors = [NSArray arrayWithObjects:(id)pinkDarkOp.CGColor,(id)pinkLightOp.CGColor,nil];
gradient.locations = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0f],[NSNumber numberWithFloat:0.7],nil];
[[img_TopBarView layer] insertSublayer:gradient atIndex:0];
[self.view addSubview:img_TopBarView];
[img_TopBarView release];
You have to take the values of pinkDarkOp and pinkLightOp as per your need. You can get this color code anywhere on google.
For eg : http://gradients.glrzad.com
For Creating the transparent UITextView you can use the alpha property of the UITextView.
I hope this helps.

CAGradientLayer not autoresizing

I added a CAGradientLayer on my UIImageView. I've set the autoresizing mask on the UIImageView to be flexible all across border (flexible height, width..etc). However the gradient layer that I added on top of my imageView doesnt resize when the UIImageView resizes. Why is this? Here's the code:
CAGradientLayer *imgOverlay = [CAGradientLayer layer];
CGColorRef startBlueColor = [UIColor colorWithRed:23/255.f green:171/255.f
blue:219/255.f alpha:0.8].CGColor;
CGColorRef endBlueColor = [UIColor colorWithRed:23/255.f green:171/255.f
blue:219/255.f alpha:0.5].CGColor;
imgOverlay.colors = [NSArray arrayWithObjects:
(id) startBlueColor,
(id) endBlueColor,
nil];
imgOverlay.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0],
[NSNumber numberWithFloat:1.0],
nil];
imgOverlay.startPoint = startPoint;
imgOverlay.frame = self.backgroundImageView_.bounds;
imgOverlay.endPoint = endPoint;
self.imageOverlay = imgOverlay;
[self.backgroundImageView_.layer addSublayer:self.imageOverlay];
CALayer does not support auto resizing on iOS. You must implement your resizing manually in layoutSubviews or wherever appropriate.

Already have a gradient on my UIView, having trouble inserting a new one

This is the code I already have:
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = backGradient.frame;
gradient.colors = [NSArray arrayWithObjects:(id)[newGradientTop CGColor], (id)[newGradientBottom CGColor], nil];
[backGradient.layer insertSublayer:gradient atIndex:0];
I'm now trying to show a new gradient instead, using the same block of code but different colours going into it. But the new gradient doesn't show, and i think this is because it will not show above the already existing gradient.
How can i fix this?
Use this function (replacing old gradient layer with the new one):
#define kGradientLayerKey #"MyGradientLayer"
void makeViewGradient(UIView *pView,CGColorRef clr1,CGColorRef clr2)
{
CAGradientLayer *gradient = [CAGradientLayer layer];
[gradient setValue:#"1" forKey:kGradientLayerKey];
gradient.frame = pView.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)clr1,(id)clr2,nil];
CALayer *pGradientLayer = nil;
NSArray *ar = pView.layer.sublayers;
for (CALayer *pLayer in ar)
{
if ([pLayer valueForKey:kGradientLayerKey])
{
pGradientLayer = pLayer;
break;
}
}
if (!pGradientLayer) [pView.layer insertSublayer:gradient atIndex:0];
else [pView.layer replaceSublayer:pGradientLayer with:gradient];
pView.backgroundColor = nil;//free memory !
}
Instead of adding additional layers, keep track of your original layer and replace it with your new one. You probably mean to use -replaceSublayer:with: here.
[backGradient.layer insertSublayer:gradient atIndex:0] inserts the new layer at the bottom of the stack, under any other layers. Try
[backGradient.layer addSublayer:gradient] instead.