I'm using this code to add a layer to a custom UIView and it works like a charm:
CGRect newrect = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
CALayer* heartBackground = [CALayer layer];
heartBackground.contents = (__bridge id)([UIImage imageNamed:#"5HeartsGray"].CGImage);
heartBackground.frame = newrect;
[self.layer addSublayer:heartBackground];
But when I tried to use it in draw method using Quartz using a new Rect(like this)
CGContextSaveGState(context);
CGRect ratingRect = CGRectMake(250, 100, 150, 20);
CALayer* heartBackground = [CALayer layer];
heartBackground.contents = (__bridge id)([UIImage imageNamed:#"5HeartsGray"].CGImage);
heartBackground.frame = ratingRect;
[heartBackground renderInContext:context];
CGContextRestoreGState(context);
It renders in the beginning of the frame and not inside of ratingRect.
If I call [heartBackground setNeedsDisplay] it disappear. The same thing with heartBackground.masksToBounds = YES
What I'm doing wrong. Do I need To switch to CGLayer because I'm using CoreGraphics?
This is the output when working with CoreGraphics (as you see the hearts starts at coordinates x=0, y=0 and normally it starts at x=250, y=100):
It doesn't make sense to create and draw a CALayer inside draw function of a UIView. CALayer is an independently drawable unit and it draws itself on its parentLayer's context by default. Which means if you really want to use separate CALayers try adding as many subLayers to self.layer (inside a UIView) as you want, set their frames and they'll get drawn when their container view shows up. Regarding your problem, here's what you can try.
Option 1: Take this code
CGRect ratingRect = CGRectMake(250, 100, 150, 20);
CALayer* heartBackground = [CALayer layer];
heartBackground.contents = (__bridge id)([UIImage imageNamed:#"5HeartsGray"].CGImage);
heartBackground.frame = ratingRect;
[self.layer addSubLayer:heartBackground];
Out of the drawRect: method and write it in initWithFrame: and in drawRect simply do [heartBackground renderInContext:UIGraphicsGetCurrentContext];
Option 2: Draw your hearts without a CALayer.
CGRect ratingRect = CGRectMake(250, 100, 150, 20);
[[UIImage imageNamed:#"5HeartsGray"] drawInRect:ratingRect];
Related
First I created the CAShapeLayer for masking the UIView at the bottom with height of 10px like this:
CAShapeLayer *mask = [[CAShapeLayer alloc]init];
CGRect maskRect = CGRectMake(0, self.view.frame.size.height-10, self.view.frame.size.width, 10.0);
CGPathRef path = CGPathCreateWithRect(maskRect, NULL);
mask.path = path;
CGPathRelease(path);
mask.anchorPoint = CGPointMake(0.5, 1.0);
self.view.layer.mask = mask;
After commenting the above code I created a CALayer for masking the UIView at the bottom with height of 10px like this:
CALayer *mask = [CALayer layer];
mask.contents = (id)[[self getImg]CGImage];
mask.frame = CGRectMake(0, self.view.frame.size.height-5, self.view.frame.size.width, 10.0);
mask.anchorPoint = CGPointMake(0.5, 1.0);
self.view.layer.mask = mask;
Question 1:
To perfectly touch the bottom not a pixel above or below, for CAShapeLayer the CGRect I defined is
CGRectMake(0, self.view.frame.size.height-10, self.view.frame.size.width, 10.0);
and for CALayer the CGRect I defined is
CGRectMake(0, self.view.frame.size.height-5, self.view.frame.size.width, 10.0);
why -10 for CAShapeLayer and why -5 for CALayer ?
The animation code and its effects on CAShapeLayer and CALayer:
CGRect oldBounds = mask.bounds;
CGRect newBounds = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height/2);
[UIView animateWithDuration:1.0 animations:^{
CABasicAnimation* revealAnimation = [CABasicAnimation animationWithKeyPath:#"bounds"];
revealAnimation.fromValue = [NSValue valueWithCGRect:oldBounds];
revealAnimation.toValue = [NSValue valueWithCGRect:newBounds];
revealAnimation.duration = 0.5;
[mask addAnimation:revealAnimation forKey:#"revealAnimation"];
}];
// Update the bounds so the layer doesn't snap back when the animation completes.
mask.bounds = newBounds;
Effect on CAShapeLayer the position is changed but no effect on hight like this:
Effect on CALayer which worked perfectly and the result I wanted:
Question 2:
Why CAShapeLayer changed the position not the height?
I don't know why but I am having a feeling like CALayer animation is not going smooth?
Question 3:
The [UIView animateWithDuration:1.0 animations:^{ ... block do not have any effect on animation, why?
Answer 1:
Not sure why it is 5 and 10 for CAShapeLayer and CALayer, need to debug closely, but maybe problem is in adjusting anchorPoint or because you use path of shape layer
Answer 2:
About effect on CAShapeLayer: this is because you assigned CGPath to it, and changed shape layer frame, but not path. So you need to set new path property with correct coordinates. Something like this:
CGRect maskRect = CGRect newBounds = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height/2);
CGPathRef path = CGPathCreateWithRect(maskRect, NULL);
mask.path = path;
CGPathRelease(path);
Answer 3:
CABasicAnimation is class, which will do all animation, and you don't need to use it in UIView animateWithDuration block.
This code should work as expected:
CABasicAnimation* revealAnimation = [CABasicAnimation animationWithKeyPath:#"bounds"];
revealAnimation.fromValue = [NSValue valueWithCGRect:oldBounds];
revealAnimation.toValue = [NSValue valueWithCGRect:newBounds];
revealAnimation.duration = 0.5;
[mask addAnimation:revealAnimation forKey:#"revealAnimation"];
I am working on an ios application and i have a issue using CALayer it has created a transparent mask on every image i put in the Layer.
Please have a look at the code below :
self.mask = [CALayer layer];
self.mask.contents = CFBridgingRelease(([UIImage imageNamed:#"launch screen.png"].CGImage));
self.mask.bounds = CGRectMake(0, 0, 200, 200);
self.mask.anchorPoint = CGPointMake(0.5, 0.5);
self.mask.position = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));
self.imageView = _imageView;
self.view.layer.mask = self.mask;
Please Refer to this image for example :
http://31.media.tumblr.com/10cc0ba92377a2cba9fb35c9943fd2ca/tumblr_inline_n6zpokNxpC1qh9cw7.gif
how can I draw a layer with a fixed angle, instead of horizontal like this:
CALayer *layer1 = [CALayer layer];
UIImage *image1 = [UIImage imageNamed:#"cuore2.png"];
[layer1 setPosition:CGPointMake(50,50)];
[layer1 setBounds:CGRectMake(10, 10, 40, 40)];
layer1.contents=(id)image1.CGImage;
[self.view.layer addSublayer:layer1];
Thanks a lot!
If you just want to display layer rotated by angle alpha (that is don't need any pseudo 3D perspective effects) you can set layer's transform:
[CATransaction setDisableActions:YES]; // Disable animation
layer1.transform = CATransform3DMakeRotation(alpha, 0, 0, 1); // Rotate layer
I've got a custom layer which is sub-layer of something else. The problem is, my layer can't know it's size until I'm in drawLayer:inContext:.
When I don't change the bounds/frame of my layer everything draws perfectly.
When I add in the the line to change frame/bounds, my background looks perfect, but my content doesn't exist. It looks like the content is clipping off the original bounds instead of the new (bigger) bounds so I see nothing but the background.
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context {
CGFloat initialGraphContentsWidth = graph.frame.size.width - graph.leftMargin;
CGFloat initialGraphContentsHeight = graph.frame.size.height - graph.bottomMargin - graphHelper.topMargin;
self.frame = CGRectMake(0, 0, initialGraphContentsWidth, initialGraphContentsHeight);
self.position = CGPointMake(graphHelper.leftMargin + 1, graphHelper.topMargin + 1);
self.backgroundColor = [UIColor greenColor].CGColor;
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
CGContextAddRect(context, CGRectMake(0, 0, 20, 20));
}
So how to I dynamically change my bounds and still get my content to appear?
I tried setting up a CATransaction with a duration of 0, but that did nothing.
Have you tried calling setNeedsDisplay after you have updated the frame/bounds?
So for instance if you had a CALayer called layer, you could try the following:
layer.frame = CGRectMake(20, 40, 100, 300);
// tell the layer that it needs to refreshed
[layer setNeedsDisplay];
I have a collection of CALayers. Each layer is a sublayer of the same parent CALayer, and each has a shadow applied to it. The layers are positioned dynamically, and there are many of them, so I can't predict how they'll be arranged ahead of time.
If the layers are adjacent to each other (close enough that they are almost touching) the shadow of one of the CALayers is rendered on top of the other CALayer. That's probably the desired effect in most cases, but I want my layers to exist in the same z-plane. (An example of this is the way CSS3 shadows are applied to block elements in web design.)
Is this possible? How can I achieve this?
(I had this idea: Adding a 'shadow' sublayer to each CALayer with my own shadow image, and setting the z-position to a lower value. But doesn't the layer-tree make this impossible? Z-positions in one layer's coordinate system are independent from z-positions in another layer's coordinate system, right?)
If all of the shadowed layers have the same shadow settings, put them into a container layer and set the shadow on the container layer. Example:
- (void)viewDidLoad
{
[super viewDidLoad];
CALayer *containerLayer = [CALayer layer];
containerLayer.frame = self.view.bounds;
containerLayer.shadowRadius = 10;
containerLayer.shadowOpacity = 1;
[self.view.layer addSublayer:containerLayer];
CAShapeLayer *layer1 = [CAShapeLayer layer];
layer1.bounds = CGRectMake(0, 0, 200, 200);
layer1.position = CGPointMake(130, 130);
layer1.path = [UIBezierPath bezierPathWithOvalInRect:layer1.bounds].CGPath;
layer1.fillColor = [UIColor redColor].CGColor;
[containerLayer addSublayer:layer1];
CAShapeLayer *layer2 = [CAShapeLayer layer];
layer2.bounds = CGRectMake(0, 0, 200, 200);
layer2.position = CGPointMake(170, 200);
layer2.path = [UIBezierPath bezierPathWithOvalInRect:layer2.bounds].CGPath;
layer2.fillColor = [UIColor blueColor].CGColor;
[containerLayer addSublayer:layer2];
}
Output: