layer with angle - ios

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

Related

CALayer draw outside of rect

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];

Setting CALayer maskToBounds causing layer to disappear

I am trying to create a circle which is filled depending upon a certain percentage. Here is pic of the effect I am going for:
I have a UIView and to this I add a CAShapeLayer which draws the circle. I am then creating another UIShapeLayer as a square to match the UIView containing the circle. Then setting the height of this depending on the figure. So if the square is 100px high and the value is 10% then I set the square to 10px so that it fills 10% of circle.
The image below shows 50% of the circle being filled. As you can see it covers the UIView as well as the circle. However when I try to set the CAShapeLayer to mask to bounds ( circle.maskToBounds) the circle complete disappears along with the square that I am adding as it's subview.
Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
[self drawCircleInView:view];
[self.view addSubview:view];
}
- (void)drawCircleInView:(UIView *)v
{
// Set up the shape of the circle
int radius = v.frame.size.height / 2;
CAShapeLayer *circle = [CAShapeLayer layer];
// Make a circular shape
circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2*radius, 2*radius)
cornerRadius:radius].CGPath;
// Center the shape in self.view
circle.position = CGPointMake(CGRectGetMidX(v.bounds)-radius,
CGRectGetMidY(v.bounds)-radius);
// Configure the apperence of the circle
circle.fillColor = [UIColor blueColor].CGColor;
circle.strokeColor = [UIColor clearColor].CGColor;
circle.lineWidth = 5;
circle.masksToBounds = YES; // HERE IS THE LINE OF CODE THAT MAKES THE CIRCLE DISAPPEAR
CAShapeLayer *sublayer = [CAShapeLayer layer];
sublayer.backgroundColor = [UIColor orangeColor].CGColor;
sublayer.opacity = .5f;
sublayer.frame = CGRectMake(0, 100, 200, 100);
[circle addSublayer:sublayer];
// Add to parent layer
[v.layer addSublayer:circle];
}
I am wondering why setting circle.masksToBounds = YES is making the circle and the sublayer disappear completely. My understanding is that by setting this it should only show the sublayer over the circle.
Many thanks in advance.
Try this:
- (void)drawCircleInView:(UIView *)v {
// Set up the shape of the circle
CGSize size = v.bounds.size;
CALayer *layer = [CALayer layer];
layer.frame = v.bounds;
layer.backgroundColor = [UIColor blueColor].CGColor;
CALayer *sublayer = [CALayer layer];
sublayer.frame = CGRectMake(0, size.height/2, size.width, size.height/2);
sublayer.backgroundColor = [UIColor orangeColor].CGColor;
sublayer.opacity = .5f;
CAShapeLayer *mask = [CAShapeLayer layer];
mask.frame = v.bounds;
mask.path = [UIBezierPath bezierPathWithOvalInRect:v.bounds].CGPath;
mask.fillColor = [UIColor blackColor].CGColor;
mask.strokeColor = [UIColor clearColor].CGColor;
[layer addSublayer:sublayer];
[layer setMask:mask];
// Add to parent layer
[v.layer addSublayer:layer];
}
maskToBounds is "to bounds" you are not setting bounds nor frame.
It's not necessary, but you'd better to set frame of CAShapeLayer, to prevent unnecessary confusion.
bounds is rectangle, not CAShapeLayer's shape. Use mask layer to mask by shape instead.
circle and sublayer is not need to be CAShapeLayer because it will be masked by shaped mask layer.
To achieve this use a square image with its center circle as transparent. like this image
How to use this image for this effect -
Add a square layer (with lighter color/ or non fill color) first. above that add a layer with the fill color with sane rect. and above all place this image. and change the frame of middle layer that has the fill color to achieve desired effect

iOS - Creating a blurred layer using CALayer

I created a CALayer with white color and 0.4 of opacity. What I want to is to make this layer above an image and make it blur, like the notification layer is blurring on the app or the home screen you're opening here's my CALayer:
CALayer *lyr = [CALayer layer];
lyr.bounds = CGRectMake(0, 0, 190, 190);
lyr.position = CGPointMake(90, 50);
lyr.backgroundcolor = [UIColor whiteColor].CGColor;
lyr.opacity = 0.4f;
[self.view.layer addSubLayer:lyr];
Sorry if I didn't explained what I need exactly
Thanks in Advance
Take a look at FXBlurView
https://github.com/nicklockwood/FXBlurView
It also provides a method to apply a tinted blur on an image.
- (UIImage *)blurredImageWithRadius:(CGFloat)radius iterations:(NSUInteger)iterations tintColor:(UIColor *)tintColor;
-> Import UIImage+FXBlurImage.h

Animating the mask for a UIView

I can create a mask like this:
CALayer *mask = [CALayer layer];
mask.contents = (id)[[UIImage imageNamed:#"mask.png"] CGImage];
mask.frame = CGRectMake(0, 0, 10, 10);
self.content.layer.mask = mask;
And this will correctly reveal the top left 10 pixels of my content (because mask.png is just a black image). However I want to animate the mask to reveal the rest of the content:
[UIView animateWithDuration:3.0
animations:^{
mask.frame = self.content.bounds;
}
completion:^(BOOL finished){
}];
The problem is that there's no animation. The entire content gets displayed immediately. Why does this happen, and how can I animate the mask so that the content is revealed from the upper left?
The frame is a derived property of various other properties, such as the position, bounds, anchorPoint, and any transform it has applied to it. It's not recommended to animate this property directly, especially when dealing with lower level CoreAnimation layers.
In this case, I would assume you want to animate the bounds. You can use the UIView animation method above, but when dealing with CALayers directly I prefer to use the CoreAnimation methods of animation.
CGRect oldBounds = mask.bounds;
CGRect newBounds = self.content.bounds;
CABasicAnimation* revealAnimation = [CABasicAnimation animationWithKeyPath:#"bounds"];
revealAnimation.fromValue = [NSValue valueWithCGRect:oldBounds];
revealAnimation.toValue = [NSValue valueWithCGRect:newBounds];
revealAnimation.duration = 3.0;
// Update the bounds so the layer doesn't snap back when the animation completes.
mask.bounds = newBounds;
[mask addAnimation:revealAnimation forKey:#"revealAnimation"];

Is there a way to prevent CALayer shadows from overlapping adjacent layers?

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:

Resources