I want to draw a layer on a view. for that i write this code.
arcLayer=[CAShapeLayer layer];
arcLayer.frame=self.circleView.frame;
arcLayer.backgroundColor = [UIColor purpleColor].CGColor;
[self.circleView.layer addSublayer:arcLayer];
but this the position of layer is not exact on the view as you can see in the following image. the green one is circleView and the purple one is arc layer.
You have a problem on this line: arcLayer.frame=self.circleView.frame;. Change that line to:
CGRect frame = self.circleView.frame;
frame.origin = CGPointZero;
arcLayer.frame=frame;
This will solve your problem. You just forgot to set origin of your layer to CGPointZero or whatever position.
Good Luck!
More concisely:
arcLayer.frame = self.circleView.bounds;
Related
I am not good with CALayer but I need to draw a plus (+) sign and I don't want to use an image as I want to animate the drawing. Any help?
After all the down votes, I was able to do it myself. Here's how for others who might need this
CGFloat height = 2.f;
CGFloat width = 3.f;
CGFloat cornerRadius = 1.f;
CALayer *hLayer = [CALayer layer];//this is the left - right stroke
hLayer.frame = CGRectMake(0, CGRectGetMidY(self.bounds)-(height/2), width, height);
hLayer.cornerRadius = cornerRadius;
CALayer *vLayer = [CALayer layer];// this is the top - bottom stroke
vLayer.frame = CGRectMake(CGRectGetMidX(self.bounds) - (height/2), -3,height, width);
vLayer.cornerRadius = cornerRadius;
[self.layer addSublayer:hLayer];
[self.layer addSublayer:vLayer];
As luk2302 says, use a CAShapeLayer.
You can install a CGPath into a CAShapeLayer. You can get a CGPath from any UIBezierPath. (It has a CGPath property that lets you get to the underlying CGPath object for any UIBezierPath.)
I suggest reading up on UIBezierPath. It has methods moveToPoint and addLineToPoint.
You'd move to the top of your plus, add a line down, then move to the left of your plus and add a line across.
Note that you can also animate images, depending on the type of animation you are after. What kind of animation do you need to do?
Another simple way would be to use two UIView with the same background color and add them as a subview to another UIView. Then you can use the UIView animateWithDuration:... method to do your simple animations.
I am wondering if it is possible to clip a view to a Bezier Path. What I mean is that I want to be able to see the view only in the region within the closed Bezier Path. The reason for this is that I have the outline of an irregular shape, and I want to fill in the shape gradually with a solid color from top to bottom. If I could make it so that a certain view is only visible within the path then I could simply create a UIView of the color I want and then change the y coordinate of its frame as I please, effectively filling in the shape. If anyone has any better ideas for how to implement this that would be greatly appreciated. For the record the filling of the shape will match the y value of the users finger, so it can't be a continuous animation. Thanks.
Update (a very long time later):
I tried your answer, Rob, and it works great except for one thing. My intention was to move the view being masked while the mask remains in the same place on screen. This is so that I can give the impression of the mask being "filled up" by the view. The problem is that with the code I have written based on your answer, when I move the view the mask moves with it. I understand that that is to be expected because all I did was add it as the mask of the view so it stands to reason that it will move if the thing it's tied to moves. I tried adding the mask as a sublayer of the superview so that it stays put, but that had very weird results. Here is my code:
self.test = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
self.test.backgroundColor = [UIColor greenColor];
[self.view addSubview:self.test];
UIBezierPath *myClippingPath = [UIBezierPath bezierPath];
[myClippingPath moveToPoint:CGPointMake(100, 100)];
[myClippingPath addCurveToPoint:CGPointMake(200, 200) controlPoint1:CGPointMake(self.screenWidth, 0) controlPoint2:CGPointMake(self.screenWidth, 50)];
[myClippingPath closePath];
CAShapeLayer *mask = [CAShapeLayer layer];
mask.path = myClippingPath.CGPath;
self.test.layer.mask = mask;
CGRect firstFrame = self.test.frame;
firstFrame.origin.x += 100;
[UIView animateWithDuration:3 animations:^{
self.test.frame = firstFrame;
}];
Thanks for the help already.
You can do this easily by setting your view's layer mask to a CAShapeLayer.
UIBezierPath *myClippingPath = ...
CAShapeLayer *mask = [CAShapeLayer layer];
mask.path = myClippingPath.CGPath;
myView.layer.mask = mask;
You will need to add the QuartzCore framework to your target if you haven't already.
In Swift ...
let yourCarefullyDrawnPath = UIBezierPath( .. blah blah
let maskForYourPath = CAShapeLayer()
maskForYourPath.path = carefullyRoundedBox.CGPath
layer.mask = maskForYourPath
Just an example of Rob's solution, there's a UIWebView sitting as a subview of a UIView called smoothView. smoothView uses bezierPathWithRoundedRect to make a rounded gray background (notice on right). That works fine.
But if smoothView has only normal clip-subviews, you get this:
If you do what Rob says, you get the rounded corners in smoothView and all subviews ...
Great stuff.
Is it possible on iOS to make a regular UIView in some CGRect and add subviews to it and then tell that container UIView something like this:
containerView.layer.path = someClosedUIBezierPath
?
And will then all of the subviews also be curved according to it's parent container view?
I know that every UIView has it's own CALayer and that would be the starting point for me.
I saw examples with animations but I don't see nothing like above (maybe because it isn't there :))
This is a little late, but maybe it will help someone:
You can clip a view to a bezier path by using a CAShapeLayer and the view's layer's mask property:
CAShapeLayer shapeMask = [[CAShapeLayer alloc] initWithFrame:containerView.bounds];
shapeMask.path = someClosedUIBezierPath.CGPath;
containerView.layer.mask = shapeMask;
[shapeMask release];
For certain reasons, I'm trying to avoid using a CAScrollLayer to do this. The effect I'm going after is to progressively reveal (from bottom to top) a CALayer's content (a png I previously loaded in). So I thought about doing this:
layer.anchorPoint = CGPointMake(.5, 1);
CABasicAnimation* a = [CABasicAnimation animationWithKeyPath:#"bounds.size.height"];
a.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
a.fillMode = kCAFillModeBoth;
a.removedOnCompletion = NO;
a.duration = 1;
a.fromValue = [NSNumber numberWithFloat:0.];
a.toValue = [NSNumber numberWithFloat:layer.bounds.size.height];
[layer addAnimation:a forKey:nil];
The problem with this is you can tell the layer's content is scaled with the bounds. I was trying for the bounds to change but the content to stay always the original size, so that effectively the bounds clip the image and as I increase bounds.height, the image "Reveals" itself.
Any ideas as to how to pull it off or what might I be missing?
Ok I got it to work, but I basically had to update the layer's frame too, to reflect the change in anchor point:
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
layer.contentsGravity = kCAGravityTop;
layer.masksToBounds = YES;
layer.anchorPoint = CGPointMake(.5, 1);
CGRect newFrame = layer.frame;
newFrame.origin.y += newFrame.size.height / 2;
layer.frame = newFrame;
[CATransaction setValue:(id)kCFBooleanFalse forKey:kCATransactionDisableActions];
a.toValue = [NSNumber numberWithFloat:layer.bounds.size.height];
[layer addAnimation:a forKey:nil];
"Dad" has the right answer.
You want to create a CAShapeLayer, and install that as the mask on your layer.
You create a CGPath that is just a rectangle and install that path into the shape layer. The contents of the path determine what areas of the masked layer show up. If the path is a triangle in the middle of the layer, then only the triangle appears.
You then create an animation that animates the path.
To reveal your image from the bottom, you'd set up a path that was a 0 height rectangle at the bottom of the layer, and then you'd create a CAAnimation where the toValue is the same rectangle with a hight of the full layer you want to reveal. The system would generate an animation that reveals the image in a sweep.
You can use this same technique to achieve all kinds of cool effects, like barn doors, venetian blinds, "iris wipes", etc.
What if you changed the clipping mask instead? (or use a mask layer).
You could put another image over the target image and move it up like a stage curtain.
I want to add a corner radius to a UIButton. It is working fine but a problem occurs when I add image to it.
It does not round its corners with images, the image is shown in full rectangle form.
Please see the image, I have used the corner radius with red color and the output is as follow:
Please help.
Did you try to use set the masksToBounds: property? Fore example:
CALayer *layer = [myView layer];
[layer setMasksToBounds:YES];
[layer setCornerRadius:8.0];
That should do the trick.
you use -
myButton.imageView.layer.cornerRadius = 5;
but make sure that your image size is exact same as button size. its working for me.
yourButton.layer.cornerRadius = 10 //this value should be half of your button's height to make a circle
yourButton.clipsToBounds = true //this clips everything outside of bounds