CAGradientLayer not autoresizing - iphone

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.

Related

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.

Animating CAGradientLayer

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?

UITableView with Transparent Gradient at Top and Bottom

I have searched this forum, Google and other forums and have not found an the answer to my particular issue.
Basically, I have a UIView which contains UITableView. I followed this tutorial and it was partially successful. The problem is the gradient. I have a background image behind the UITableView. So as the cell nears the gradient, I want the background to be showing, instead of the white.
I also found this post which which is where I found the tutorial, but I didn't want to hijack that post with my own questions for matt.
Any help in the right direction would be great!
EDIT1: I know I can use another image with the background image and the middle cut out, but I'm looking for a solution which AVOIDS using PNGs, if possible.
EDIT2: Here's an image of what I get now:
EDIT3:
Here is my code:
Header:
#interface MyView : UIViewController {
CAGradientLayer *_maskLayer;
UITableView *_tableView;
}
#property (nonatomic, retain) CAGradientLayer *maskLayer;
#property (nonatomic, retain) IBOutlet UITableView *tableView;
Implementation:
#implementation HighScoresView_iPhone
#synthesize tableView = _tableView;
#synthesize maskLayer = _maskLayer;
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (![self maskLayer]) {
[self setMaskLayer:[CAGradientLayer layer]];
CGColorRef outerColor = [UIColor colorWithWhite:1.0 alpha:1.0].CGColor;
CGColorRef innerColor = [UIColor colorWithWhite:1.0 alpha:0.0].CGColor;
[[self maskLayer] setColors:[NSArray arrayWithObjects:
(id)outerColor,
(id)innerColor,
(id)innerColor,
(id)outerColor,
nil
]
];
[[self maskLayer] setLocations:[NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0],
[NSNumber numberWithFloat:0.2],
[NSNumber numberWithFloat:0.8],
[NSNumber numberWithFloat:1.0],
nil
]
];
[[self maskLayer] setBounds:CGRectMake(0, 0, [[self scoreTableView] frame].size.width, [[self scoreTableView] frame].size.height)];
[[self maskLayer] setAnchorPoint:CGPointZero];
[[[self scoreTableView] layer] addSublayer:[self maskLayer]];
}
}
You can do this with the CALayer mask property. But you can't set the mask on the table view's own layer because that mask will scroll with the rows of the table. Instead, put your table view inside a new superview (of class UIView) that you create just for this. Call it tableMaskView.
The new superview should have its backgroundColor property set to UIColor.clearColor and its opaque property set to NO. Then you can set its mask like this:
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = self.tableMaskView.bounds;
gradient.colors = [NSArray arrayWithObjects:
(__bridge id)UIColor.clearColor.CGColor,
UIColor.whiteColor.CGColor,
UIColor.whiteColor.CGColor,
UIColor.clearColor.CGColor,
nil];
gradient.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0],
[NSNumber numberWithFloat:1.0/16],
[NSNumber numberWithFloat:15.0/16],
[NSNumber numberWithFloat:1],
nil];
self.tableMaskView.layer.mask = gradient;
Using a layer mask is not the most efficient way, but it's the easiest to program. Test whether it's fast enough.
Swift 3 version of rob mayoff's answer:
let gradient = CAGradientLayer()
gradient.frame = tableMaskView.bounds
gradient.colors = [UIColor.clear.cgColor, UIColor.white.cgColor,
UIColor.white.cgColor, UIColor.clear.cgColor]
gradient.locations = [0 as NSNumber, 1.0/16.0 as NSNumber,
15.0/16.0 as NSNumber, 1 as NSNumber]
tableMaskView.layer.mask = gradient
Sounds like the background color of your table view is set to white. Try setting the background of your table view to transparent:
tableView.backgroundColor = [ UIColor clearColor ] ;
tableView.opaque = NO ;
Replace this line:
[[[self scoreTableView] layer] addSublayer:[self maskLayer]];
With this one:
self.scoreTableView.layer.mask = self.maskLayer;
(Or if you insist on your syntax-styling even thought I can't see why) :
[[[self scoreTableView] layer] setMask:[self maskLayer]];
Also this answer might help you.

Fixing CALayer at top and bottom of UIScrollView

I am subclassing UIScrollView and adding some visual elements. At the top, there should be a gradient going from black to clear, and at the bottom there is a mask that fades out towards the bottom. I have those layers added and looking right, but when I scroll, they stay at the coordinates I put them in (with respect to the scroll view), rather than being "fixed" to the bottom and top of the view. This scroll view only scrolls vertically.
Here is the code for SettingsScrollView.m:
#import "SettingsScrollView.h"
#import <QuartzCore/QuartzCore.h>
#define SHADOW_HEIGHT 20.0
#define SHADOW_INVERSE_HEIGHT 10.0
#define SHADOW_RATIO (SHADOW_INVERSE_HEIGHT / SHADOW_HEIGHT)
#implementation SettingsScrollView
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
[self setUpShadow];
return self;
}
- (CAGradientLayer *)shadowAsInverse:(BOOL)inverse
{
CAGradientLayer *newShadow = [[[CAGradientLayer alloc] init] autorelease];
CGRect newShadowFrame = CGRectMake(0, 0, self.frame.size.width, inverse ? SHADOW_INVERSE_HEIGHT : SHADOW_HEIGHT);
newShadow.frame = newShadowFrame;
CGColorRef darkColor =[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:inverse ? (SHADOW_INVERSE_HEIGHT / SHADOW_HEIGHT) * 0.5 : 0.5].CGColor;
CGColorRef lightColor = [self.backgroundColor colorWithAlphaComponent:0.0].CGColor;
newShadow.colors = [NSArray arrayWithObjects: (id)(inverse ? lightColor : darkColor), (id)(inverse ? darkColor : lightColor), nil];
return newShadow;
}
- (CAGradientLayer *)gradientMask
{
CAGradientLayer *mask = [[[CAGradientLayer alloc] init] autorelease];
CGRect maskFrame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
mask.frame = maskFrame;
CGColorRef darkColor =[UIColor clearColor].CGColor;
CGColorRef lightColor =[UIColor blackColor].CGColor;
mask.colors = [NSArray arrayWithObjects: (id)lightColor, (id)lightColor, (id)darkColor, nil];
mask.locations = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0], [NSNumber numberWithFloat:0.9], [NSNumber numberWithFloat:1.0], nil];
return mask;
}
- (void)setUpShadow
{
CAGradientLayer *topShadowLayer = [self shadowAsInverse:NO];
CAGradientLayer *bottomShadowLayer = [self gradientMask];
[self.layer insertSublayer:topShadowLayer atIndex:0];
[self.layer setMask:bottomShadowLayer];
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
CGRect topShadowLayerFrame = topShadowLayer.frame;
topShadowLayerFrame.size.width = self.frame.size.width;
topShadowLayerFrame.origin.y = 0;
topShadowLayer.frame = topShadowLayerFrame;
CGRect bottomShadowLayerFrame = bottomShadowLayer.frame;
bottomShadowLayerFrame.size.width = self.frame.size.width;
bottomShadowLayerFrame.origin.y = self.frame.size.height - bottomShadowLayer.frame.size.height;
bottomShadowLayer.frame = bottomShadowLayerFrame;
[CATransaction commit];
}
#end
I know one solution for the top could just be to add a separate view that contains a gradient, but for the bottom I believe I need to use a mask to have it do what I want it to (fade out into the background at the very bottom). The background is an image so I can't just fade to white or another color, it needs to fade to clear. I've been looking for a method that gets called when the scroll view is moved and use that to change the position of the mask, but I haven't found anything yet. Any suggestions?
You could probably accomplish this by overriding -layoutSubviews on the UIScrollView, which is invoked when its bounds change.
Another technique I've seen on views like this is that instead of doing this layer management from the view, subclass CALayer, use your subclass as the scroll view's layer, and then in your layer's -layoutSublayers, do to the same work when it moves around. I mention this latter technique, because it seems somewhat more natural for the layer to be managing its sublayers vs the view managing its layers sublayers directly.

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.