CAGradientLayer with shouldRasterize causes unwanted line - ios

I use CAGradientLayer in header view of TableView. To make scroll smooth I've turned shouldRasterize property to YES and it's caused the problem.
Gradient layer is drawn like it has a border. All frames I set in layoutSubviews and all its fields are integers.
Here are screenshots:
How it appears:
How it have to look like:
Here is a code related to this gradient layer
- (void)createShadow {
CAGradientLayer *shadowLayer= [CAGradientLayer layer];
UIColor *startColor = [UIColor colorWithWholeRed:236 green:235 blue:236];
UIColor *destinationColor = [UIColor colorWithWholeRed:248 green:248 blue:250];
shadowLayer.borderColor = [UIColor clearColor].CGColor;
shadowLayer.borderWidth = 0;
shadowLayer.colors = #[(id)startColor.CGColor,(id)destinationColor.CGColor];
shadowLayer.shouldRasterize = YES;
[self.layer addSublayer:shadowLayer];
self.shadowLayer = shadowLayer;
}
- (void)updateShadow {
CGRect newRect = CGRectMake(0, 0,
self.frame.size.width, ceil(self.frame.size.height * 0.3));
[self.shadowLayer setFrame:newRect];
}
- (void)layoutSubviews {
[super layoutSubviews];
[self updateShadow];
}
Tell me please if you know how to solve this problem)

Related

Objective-c - Horizontal UIScrollview shadow follows scrolling

I added a shadow on an horizontal uislider but when I scroll the shadow follows scrolling.
This is an explanatory image:
And this is the code:
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:tabBarScroller.bounds];
tabBarScroller.layer.masksToBounds = NO;
tabBarScroller.layer.shadowColor = [UIColor blackColor].CGColor;
tabBarScroller.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
tabBarScroller.layer.shadowOpacity = 0.5f;
tabBarScroller.layer.shadowPath = shadowPath.CGPath;
tabBarScroller.bounces = NO;
tabBarScroller.alwaysBounceHorizontal = YES;
I already tried to put a fixed size but the shadow disappears.

How to animate resizing of a UIView which uses a CAShapeLayer?

Intention:
I want to draw a circle within a view that I can move, resize and re-colour from the ViewController.
Setup:
I subclassed UIView to create a view to just hold the circle:
#implementation CircleView
+ (Class)layerClass //override
{
return [CAShapeLayer class];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.opaque = NO;
CAShapeLayer *layer = (CAShapeLayer *)self.layer;
layer.fillColor = nil;
layer.strokeColor = [UIColor colorWithWhite:1 alpha:1].CGColor;
layer.lineWidth = 2;
}
return self;
}
-(void) layoutSubviews
{
CAShapeLayer *layer = (CAShapeLayer *)self.layer;
layer.path = [UIBezierPath bezierPathWithOvalInRect:self.bounds].CGPath;
}
#end
I then initialise it like so in the parent view initWithFrame:
{
CGFloat diam = MIN(frame.size.width, frame.size.height);
self.circleView = [[VCCCircleView alloc] initWithFrame:ASRectCenteredOnPoint(ASRectGetCenter(frame), (CGSize){diam,diam})];
self.circleView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin;
[self addSubview:self.circleView];
}
Finally the parent view also provides a method for animating the view:
-(void)setCircleFrame:(CGRect)frame
{
CAShapeLayer *layer = (CAShapeLayer *)self.circleView.layer;
[UIView animateWithDuration:1 delay:10 options:UIViewAnimationOptionLayoutSubviews animations:^(void)
{
circleView.frame = frame;
//for debug: circleView.frame = CGRectInset(circleView.frame, -30, -30);
} completion:nil];
}
The idea being that you can just set the frame and the circle will move and resize. Simple right?
Problem:
The circle does *not*animate it's change in size but it does animate it's change in position. - the result being it jumps to the new size, then animates the movement of the origin. This is particularly obvious with the large delay I introduced to the animation.
I notice that if i just change the location of the circle, layoutSubviews of the circleView is not called but if I change the bounds it is.
I am not using any transform.
What I've tried:
Removing the autoresizemask stuff from the initialisation of the circle view
Various combinations of options to the animation (including: BeginFromCurrentState)
Calling [self layoutSubviews] in the animation block after the new frame is set
Editing the bounds rather than the frame
Combinations of the above
What I haven't tried:
Using a CABasicAnimation directly. Is this necessary? 'Frame' is
supposed to be animatable of UIView?

Set UIImageView frame in initWithCoder

I am trying to set the frame size of an UIImageView inside a subclass of UICollectionCell but it does not work. I use storyboard.
In the code, everything works but the last line regarding the imageView.
If i put the code in drawRect it works ok.
- (id)initWithCoder:(NSCoder *)decoder {
if ((self = [super initWithCoder:decoder]))
{
self.backgroundColor = [UIColor colorWithWhite:0.85f alpha:1.0f];
self.layer.borderColor = [UIColor lightGrayColor].CGColor;
self.layer.borderWidth = 1.0f;
self.layer.cornerRadius = 8.0f;
self.layer.masksToBounds = NO;
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowRadius = 3.0f;
self.layer.shadowOffset = CGSizeMake(0.0f, 2.0f);
self.layer.shadowOpacity = 0.5f;
self.imageView.frame = CGRectMake(10, 10, 50, 50);
}
That is because your imageView (I believe it's and IBOutlet) is not yet initialized, it's still nil. Be aware that some of you variables/properties are being initialized inside init's method. Set you breakpoint and you will see. Try set the imageView values somewhere else such as drawRect (as you suggest) or awakeFromNib.
Is the image view being moved later by a different object? Try moving that code into layoutSubviews
Add this method:
- (void) layoutSubviews
{
[super layoutSubviews];
self.imageView.frame = CGRectMake(10, 10, 50, 50);
}

iOS: Color-changing circle button

I'm trying to make a button that will show/hide a color-selection slider. That's the easy part.
What I am not sure how to do is to make the button be a circle with an outline (say black) and the fill be the color shown in the slider. As the user moves the slider, the color of the circle should change accordingly.
I am new to iOS development, so probably I am approaching this all wrong. I would really appreciate any ideas even they are completely different from what I have here.
I started to approach this with a subclass using QuartzCore layers, and the result is decent, but there are issues/things that bother me:
For some reason I get no highlight state when pressing the button
It's obviously odd to use rounded corners to achieve a circle
Unfortunately, it seems at the point were I draw the layers, the button is not laid out yet so I have had to hardcode the radius instead of making it based on the button size.
So, yeah I hardly think this approach is ideal. I will greatly appreciate any and all help. Thank you!
#import "ColorPickerButton.h"
#implementation ColorPickerButton
#pragma mark - UIButton Overrides
+ (ColorPickerButton *)buttonWithType:(UIButtonType)buttonType
{
return [super buttonWithType:UIButtonTypeCustom];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
r = 0.3;
g = 0.6;
b = 0.9;
[self drawOutline];
[self drawColor];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)layoutSubviews
{
_outlineLayer.frame = CGRectInset(self.bounds, 5, 5);
_colorLayer.frame = CGRectInset(self.bounds, 5+_outlineLayer.borderWidth, 5+_outlineLayer.borderWidth);
[super layoutSubviews];
}
#pragma mark - Layer setters
- (void)drawOutline
{
if (!_outlineLayer)
{
_outlineLayer = [CALayer layer];
_outlineLayer.cornerRadius = 20.0;
_outlineLayer.borderWidth = 3;
_outlineLayer.borderColor = [[UIColor colorWithRed:170.0/255.0 green:170.0/255.0 blue:170.0/255.0 alpha:1.0] CGColor];
_outlineLayer.opacity = 0.6;
[self.layer insertSublayer:_outlineLayer atIndex:1];
}
}
- (void)drawColor
{
if (!_colorLayer)
{
_colorLayer = [CALayer layer];
}
_colorLayer.cornerRadius = 16.0;
_colorLayer.backgroundColor = [[UIColor colorWithRed:r green:g blue:b alpha:1.0] CGColor];
_colorLayer.opacity = 1.0;
[self.layer insertSublayer:_colorLayer atIndex:2];
}
- (void)changeColorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue
{
r = red;
g = green;
b = blue;
[self drawColor];
}
#end
We do this in our app. We use the CALayer on the button and add rounded corners 1/2 the size of the component width. We add a border to the button and then set the button background color to achieve the 'fill' color. Works well.

Replace a CALayer with a CAGradientLayer doesn't work

I'm developing an iOS app with latest SDK.
I want to change a background layer dynamically:
#import <QuartzCore/QuartzCore.h>
#interface MyClass : UIView
{
#private
CALayer* _gradientBackground;
}
And some methods:
- (CALayer*)createLayerWithColor:(UIColor*)color
{
CALayer* layer = [CALayer layer];
layer.frame = CGRectMake(NSLayerX, NSLayerY,
NSLayerWidth, NSLayerHeight);
layer.backgroundColor = [color CGColor];
layer.cornerRadius = NSCornerRadius;
return layer;
}
- (CAGradientLayer*)createLayerWithGradient:(UIColor*)startColor
endColor:(UIColor*)endColor
{
CAGradientLayer* gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = CGRectMake(NSLayerX, NSLayerY,
NSLayerWidth, NSLayerHeight);
gradientLayer.colors =
[NSArray arrayWithObjects:(id)[startColor CGColor],
(id)[endColor CGColor], nil];
gradientLayer.cornerRadius = NSCornerRadius;
return gradientLayer;
}
- (void)changeBackgroundWithLayer:(CALayer*)newLayer
{
if (_gradientBackground != nil)
[_gradientBackground removeFromSuperlayer];
_gradientBackground = newLayer;
[self.layer insertSublayer:newLayer atIndex:0];
}
And I do this to change background layer:
[self changeBackgroundWithLayer:[self createLayerWithGradient:startColor endColor:endColor]];
And sometimes with this:
[self changeBackgroundWithLayer:[self createLayerWithColor:newColor]];
The way I do is:
First solid layer, next gradient layer and finally solid layer.
I have also tried with this code with no result:
- (void)changeBackgroundWithLayer:(CALayer*)newLayer
{
if (_gradientBackground != nil)
[self.layer replaceSublayer:_gradientBackground with:newLayer];
else
[self.layer insertSublayer:newLayer atIndex:0];
_gradientBackground = newLayer;
}
But it doesn't work.
Any advice?
Try this
- (void)changeBackgroundWithLayer:(CALayer*)newLayer
{
[_gradientBackground removeFromSuperlayer], _gradientBackground = nil;
[self.layer insertSublayer:newLayer atIndex:0];
_gradientBackground = newLayer;
}
This will remove the _gradientBackground and set it to nil in every case, which is perfectly acceptable in Objective-C. The newLayer is added to the layer hierarchy and the gradientBackground layer is updated.

Resources