replaceSublayer example - iphone

I have the following code and I can't seem to get it to work.
First I create a view with a layer.
UIView *view = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 100)] autorelease];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor blackColor] CGColor], (id)[[UIColor whiteColor] CGColor], nil];
[view.layer insertSublayer:gradient atIndex:0];
Next I need to replace that layer. I have tried the following and it fails because I don't know how to access that layer 0. At this point I am in a different part of the program so I cannot just call gradient. I need to extricate it from view somehow.
[view.layer replaceSublayer:0 with:newgradient];
Apparently the 0 is supposed to be Old layer but I don't know how to access it.
Thanks.

I think you should be able to use this:
[view.layer replaceSublayer:[[view.layer sublayers] objectAtIndex:0]
with:newGradient];
I'm away from my Xcode development environment and can't test this at the moment though.

Keep a pointer to the gradient layer. If you have alot of them do it with an array.
If you still don't want to do that you can always use this to get an array of all the layers..
[self.layer sublayers];
Then you can cycle through and check some parameter to see if they are the same. Like set the layer.name to something when you create it.

Here is the function :
#define MyGradientLayerName #"MyGradient"
void makeViewGradient(UIView *pView,BOOL bRemoveBackground,CGColorRef clr1,CGColorRef clr2)
{
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.name = MyGradientLayerName;
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.name isEqualToString:MyGradientLayerName])
{
pGradientLayer = pLayer;
break;
}
}
if (!pGradientLayer) [pView.layer insertSublayer:gradient atIndex:0];
else [pView.layer replaceSublayer:pGradientLayer with:gradient];
if (bRemoveBackground) pView.backgroundColor = nil;//free memory !
}

Related

adding shadow to bottom of UINavigationBar only

I have the following code:
self.navigationBar_.layer.shadowColor = [UIColor blackColor].CGColor;
self.navigationBar_.layer.shadowOpacity = 0.3f;
self.navigationBar_.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
self.navigationBar_.layer.shadowRadius = 3.0f;
self.navigationBar_.layer.masksToBounds = NO;
and I basically only want to add the border to the bottom only and not the entire rectangle. How do I do this? The code above will add a shadow to the left, right, top border also.
Instead of a layer shadow, you could just use a gradient. You could just use a transparent PNG gradient, but here is an example of how to do it programmatically:
UIView *topShadowView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.navigationBar.bounds.size.width, 10)];
CAGradientLayer *topShadow = [CAGradientLayer layer];
topShadow.frame = CGRectMake(0, 0, self.navigationBar.bounds.size.width, 10);
topShadow.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithWhite:0.0 alpha:0.25f] CGColor], (id)[[UIColor clearColor] CGColor], nil];
[topShadowView.layer insertSublayer:topShadow atIndex:0];
[self.view addSubview:topShadowView];

UITableViewCell doesn't pickup UITableViewCellSelectionStyleBlue when adding CAGradientLayer sublayer

I have a UITableViewCell that I've added a gradient to by using CAGradientLayer. This works fine, but the table cell doesn't turn blue when selected, even after setting it's selectionStyle to UITableViewCellSelectionStyleBlue. If I don't add the gradient layer, it works fine.
Is there a way to make these items work together?
Here's my code inside of cellForRowAtIndexPath:
//cell gradient
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = CGRectMake(10, 0, 300, 50);
gradient.colors = [NSArray arrayWithObjects:
(id)[[UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1] CGColor],
(id)[[UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:1] CGColor],
(id)[[UIColor colorWithRed:245/255.0 green:245/255.0 blue:245/255.0 alpha:1] CGColor],
(id)[[UIColor colorWithRed:247/255.0 green:247/255.0 blue:247/255.0 alpha:1] CGColor],
nil];
gradient.locations = [NSArray arrayWithObjects:
(id)[NSNumber numberWithFloat:0.00],
(id)[NSNumber numberWithFloat:0.50],
(id)[NSNumber numberWithFloat:0.51],
(id)[NSNumber numberWithFloat:1.0],
nil];
[cell.layer insertSublayer:gradient atIndex:0];
//bring back rounded corners by creating a masklayer
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:gradient.bounds byRoundingCorners:UIRectCornerBottomRight|UIRectCornerBottomLeft cornerRadii:CGSizeMake(8, 8)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = gradient.bounds;
maskLayer.path = maskPath.CGPath;
gradient.mask = maskLayer;
//add the cell shadow
cell.layer.shadowColor = [[UIColor blackColor] CGColor];
cell.layer.shadowRadius = 3;
cell.layer.shadowOpacity = 0.5;
cell.layer.shadowOffset = CGSizeMake(0, 0);
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
It looks like you might be putting the gradient over the view that changes when it is selected- the UITableViewCellSelectionStyleBlue view may be appearing, but you just cannot see it (a way to test it out is only have the gradient cover part of your cell- if the other part changes color when it is selected, then this is the issue).
If this is the issue, then you could do your own custom drawing for the cell when it is selected by subclassing UITableViewCell, or you could have the gradient disappear whenever the cell is selected (and reappear when the cell is deselected).

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.

CALayer w/ Animation as UIViews layer

I have a UIView & I want that view's layer to be a continuously looping animation but I'm having some trouble getting started. I have the following in my view subclass:
+ (Class)layerClass {
return [CALayer class];
}
Then in my viewController I have:
CAGradientLayer *gradient = [CAGradientLayer layer];
[gradient setColors:[NSArray arrayWithObjects:[UIColor redColor],[UIColor blueColor],nil]];
[[[self view] layer] addSublayer:gradient];
This crashes my app. What exactly am I doing wrong? The app crashes w/ EXC_BAD_ACCESS.
Actually the crash you are having isn't related to not setting locations. The issue has to do with the fact that you're passing the wrong types in your setColors array. A CGColorRef is the types that is expected. Your code should look like this:
CAGradientLayer *gradient = [CAGradientLayer layer];
[gradient setColors:[NSArray arrayWithObjects:(id)[[UIColor redColor] CGColor],
(id)[[UIColor blueColor] CGColor],nil]];
[[[self view] layer] addSublayer:gradient];
The locations parameter of a CAGradientLayer is optional.
Best regards.
You forget to specify the locations for colors. By default, the location is nil.
CAGradientLayer *gradient = [CAGradientLayer layer];
[gradient setColors:[NSArray arrayWithObjects:
[[UIColor redColor] CGColor],
[[UIColor blueColor] CGColor],
nil]];
[gradient setLocations:
[NSNumber numberWithFloat:0.0], // for redColor
[NSNumber numberWithFloat:1.0], // for blueColor
nil]];
[[[self view] layer] addSublayer:gradient];

CABasicAnimation in a CAGradientLayer not working - what am I doing wrong?

I have a custom UITableView cell which has a button and a label. I fire a method when someone taps on the button, and then color that row. It's all working fine.
What I want to actually do is
user taps button, the row is colored in a gradient (it works now)
The gradient fades away
My code is below (BackView is the view in my custom cell)
CAGradientLayer *layer = [CAGradientLayer layer];
layer.frame = BackView.bounds;
UIColor *cOne = [UIColor paleYellowColor];
UIColor *cTwo = [UIColor whiteColor];
NSArray *colors = [NSArray arrayWithObjects:(id)cOne.CGColor,
cTwo.CGColor, nil];
layer.colors = colors;
NSNumber *stopOne = [NSNumber numberWithFloat:0.00];
NSNumber *stopTwo = [NSNumber numberWithFloat:0.8];
NSArray *locations = [NSArray arrayWithObjects:stopOne, stopTwo, nil];
layer.locations = locations;
CABasicAnimation *animateLayer = [CABasicAnimation animationWithKeyPath:#"colors"];
animateLayer.fromValue = [UIColor paleYellowColor];
animateLayer.toValue = [UIColor whiteColor];
animateLayer.duration = 3.0;
animateLayer.removedOnCompletion = YES;
animateLayer.fillMode = kCAFillModeBoth;
animateLayer.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[layer addAnimation:animateLayer forKey:#"animation"];
[BackView.layer insertSublayer:layer atIndex:0];
With this code, when I touch the button on the row, the background gets a gradient, but it never fades away, there's no animation - nothing. What am I doing wrong? I tried a few permutations and saw some examples, but none that helped me get this working.
Thanks!
When you animate a property with an explicit animation, you have provide the type that property is expecting. The colors property is animatable, however, you are giving it a UIColor for the from and to values. It's expecting an NSArray. Also, you need CGColorRefs for the colors themselves. I haven't tried it, but I'm thinking you need to change your to and from lines to:
animateLayer.fromValue = [NSArray arrayWithObjects:
(id)[[UIColor paleYellowColor] CGColor],
(id)[[UIColor whiteColor] CGColor], nil];
animateLayer.toValue = [NSArray arrayWithObjects:
(id)[[UIColor whiteColor] CGColor],
(id)[[UIColor whiteColor] CGColor], nil];
In theory, this should fade from your yellow/white gradient to white/white which should give the effect of fading out.
Best regards.