CAShaperLayer outlook change at initial point? - ios

I have made the Circle like this in my iOS Demo App.
http://www.pastebin.ca/3119326
like below image
I want a needle shape crossing 90 degree to line which is being made. like below image, which I am unable to find via code.
How can achieve this, any suggestion.
Thanks.

Pseudo Code/Real Code, here's how the layout would work, just a quick sample:
a line:
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
[bezierPath moveToPoint: CGPointMake(43.5, 30.5)];
[bezierPath addCurveToPoint: CGPointMake(166.5, 74.5) controlPoint1: CGPointMake(166.5, 74.5) controlPoint2: CGPointMake(166.5, 74.5)];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
CAShapeLayer * firstNameCorners = [CAShapeLayer layer];
[firstNameCorners setPath:bezierPath];
[[self.view layer] addSublayer:firstNameCorners];
run spin animation:
- (void)spinUntilYouThrowUp:(CAShapeLayer*)shapeLayer duration:(CGFloat)duration rotations:(CGFloat)rotations repeat:(float)repeat;
{
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2.0 * rotations * duration ];
rotationAnimation.duration = duration;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = repeat;
[shapeLayer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
}

Related

Animation for Layer in iOS

I masked an image with BezierPath.
I want to animate that mask image.
My code is as follow:
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(84.95, 205.11)];
[bezierPath addCurveToPoint: CGPointMake(122.89, 232.14) controlPoint1: CGPointMake(84.95, 205.11) controlPoint2: CGPointMake(110.81, 223.56)];
[bezierPath addCurveToPoint: CGPointMake(124.33, 233.04) controlPoint1: CGPointMake(123.37, 232.46) controlPoint2: CGPointMake(123.85, 232.78)];
[bezierPath closePath];
CAShapeLayer *maskImage = [CAShapeLayer layer];
maskImage.path = bezierPath.CGPath;
maskImage.fillColor = [[UIColor blackColor] CGColor];
_myImageView.layer.mask = maskImage;
CAShapeLayer *border = [CAShapeLayer layer];
border.path = bezierPath.CGPath;
border.strokeColor = [UIColor redColor].CGColor;
border.fillColor = [[UIColor clearColor] CGColor];
border.lineWidth = 5;
[_myImageView.layer addSublayer:border];
How can I animate this?
Thank you
Use following piece of code:
#property (nonatomic, retain) CAShapeLayer *animationLayer;
- (IBAction)drawPattern:(id)sender{
[self setup];
[self.animationLayer removeAllAnimations];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 10.0;
pathAnimation.fromValue = #(0);
pathAnimation.toValue = #(1);
[self.animationLayer addAnimation:pathAnimation forKey:#"strokeEnd"];
}
- (void)setup{
[self.animationLayer removeFromSuperlayer];
self.animationLayer = nil;
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(84.95, 205.11)];
[bezierPath addCurveToPoint: CGPointMake(122.89, 232.14) controlPoint1: CGPointMake(84.95, 205.11) controlPoint2: CGPointMake(110.81, 223.56)];
[bezierPath addCurveToPoint: CGPointMake(124.33, 233.04) controlPoint1: CGPointMake(123.37, 232.46) controlPoint2: CGPointMake(123.85, 232.78)];
CAShapeLayer *pathLayer = [CAShapeLayer layer];
// provide frame & bounds so that path can be drawn over that.
pathLayer.frame = CGRectMake(35, 100, 250, 200);
pathLayer.bounds = CGRectMake(35, 100, 250, 200);
pathLayer.path = bezierPath.CGPath;
pathLayer.strokeColor = [UIColor redColor].CGColor;
pathLayer.fillColor = [[UIColor clearColor] CGColor];
pathLayer.lineWidth = 6.f;
pathLayer.lineJoin = kCALineJoinRound;
[self.view.layer addSublayer:pathLayer];
[self setAnimationLayer:pathLayer];
}
Please let me know if this works for you & also if anything else comes up.
At the end of your code try below one
CAShapeLayer *pathLayer;
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [aPath CGPath];
shapeLayer.strokeColor = [[UIColor greenColor] CGColor];
shapeLayer.fillColor = nil;
shapeLayer.lineWidth = 5.0f;
shapeLayer.lineJoin = kCALineJoinBevel;
[myImageView.layer addSublayer:shapeLayer];
pathLayer = shapeLayer;
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 3.0;
pathAnimation.fromValue = #(0.0f);
pathAnimation.toValue = #(1.0f);
[pathLayer addAnimation:pathAnimation forKey:#"strokeEnd"];
We can animate the image with path using CABasicAnimation.
CABasicAnimation has position,path,transform,opacity,shadow......which including all layers keypaths,shape layer keypaths,CA layer keypaths,Text layer keypaths,Mask layer keypaths,UIView layer keypaths,Effect,Emit and replicator keypaths.
According to your question for drawing path we can use path and strokeEnd.But most prepare strokeEnd for accessing shape style properties.
So I think strokeEnd is the best one for animating the mask image with bezier path.
Below I give the code for path and strokeEnd also I diffrecicate both.
If I use animationWithKeyPath is path
The shape to be rendered and animatable.It is specifying the shape path.
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"path"];
animation.fromValue = #(0);
animation.toValue = #(1);
animation.duration = 6.0f;
[maskImage addAnimation:animation forKey:#"animatePath"];
The Path is a collection of individual components like lines and arcs that can be drawn and also animated.It is fundamental concept of Quartz 2D.
In below code I use strokeEnd
Generally if we want to access the shape style properties we can use
strokeEnd
The relative location at which to stop stroking the path and animatable.
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
animation.fromValue = #(0);
animation.toValue = #(1);
animation.duration = 6.0f;
[maskImage addAnimation:animation forKey:#"strokeEnd"];
The value of this property must be in the range 0.0 to 1.0. The default value of this property is 1.0.
Also strokeEnd defines following thing
Combined with the strokeStart property, this property defines the subregion of the path to stroke. The value in this property indicates the relative point along the path at which to finish stroking while the strokeStart property defines the starting point.

UIButton Top and Bottom Border additional line

I am trying to add a top and bottom border to a uibutton my function to add the border looks like this
CALayer *topBorder = [CALayer layer];
topBorder.frame = CGRectMake(os, 1.0f, b.bounds.size.width - (os * 2.0f), 1.0f);
topBorder.backgroundColor = [c1 CGColor];
[b.layer addSublayer:topBorder];
CALayer *bottomBorder = [CALayer layer];
bottomBorder.frame = CGRectMake(os, b.bounds.size.height, b.bounds.size.width - (os * 2.0f), 1.0f);
bottomBorder.backgroundColor = [c1 CGColor];
[b.layer addSublayer:bottomBorder];
//os..offset, b..uibutton, c1..color
this works fine when i call the function in viewDidAppear (but there is a delay then) but when i put it in viewdidlayoutsubviews it adds an additional line, what somehow looks like this
i set it up with a leading and trailing space to its superview, what a i doing wrong here?
The viewDidAppear is executed when you view appears on screen and every time come back from other view controller in your navigation flow. So your code add lines several times. You can review the view controller lifecycle here.
If you want to remove the "delay" and executed once, write your code in the viewDidLoad.
UPDATE
I see two approach. Create a UIButton subclass and then:
If you want to keep working with CALayers, you'll need to call a redraw method always your button change its size. If the change is animated will appear weird artifact.
The good one. I recommend you use the 'drawRect' to paint this lines and use the button frame to calculate line XY dynamically. If you button change its size, animated or not… no problem, Core Animation do all the job.
- (void)drawRect:(CGRect)rect
{
// Frames
CGRect frame = self.bounds;
// Parameters (change these values if you want to change appearance)
UIColor *lineColor = [UIColor colorWithRed: 0.5 green: 0.5 blue: 0.5 alpha: 1];
CGFloat topRatioPositionY = 0.02;
CGFloat middleRatioPositionY = 0.6;
CGFloat bottomRatioPositionY = 0.98;
CGFloat middleRatioPositionX = 0.33333;
CGFloat lineWidth = 1.0;
//// Top line
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(CGRectGetMinX(frame), CGRectGetMinY(frame) + topRatioPositionY * CGRectGetHeight(frame))];
[bezierPath addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 1.00000 * CGRectGetWidth(frame), CGRectGetMinY(frame) + topRatioPositionY * CGRectGetHeight(frame))];
[lineColor setStroke];
bezierPath.lineWidth = lineWidth;
[bezierPath stroke];
// Middle line
UIBezierPath* bezier2Path = [UIBezierPath bezierPath];
[bezier2Path moveToPoint: CGPointMake(CGRectGetMinX(frame), CGRectGetMinY(frame) + bottomRatioPositionY * CGRectGetHeight(frame))];
[bezier2Path addLineToPoint: CGPointMake(CGRectGetMinX(frame) + 1.00000 * CGRectGetWidth(frame), CGRectGetMinY(frame) + bottomRatioPositionY * CGRectGetHeight(frame))];
[lineColor setStroke];
bezier2Path.lineWidth = lineWidth;
[bezier2Path stroke];
// Bottom Line
UIBezierPath* bezier3Path = [UIBezierPath bezierPath];
[bezier3Path moveToPoint: CGPointMake(CGRectGetMinX(frame), CGRectGetMinY(frame) + middleRatioPositionY * CGRectGetHeight(frame))];
[bezier3Path addLineToPoint: CGPointMake(CGRectGetMinX(frame) + middleRatioPositionX * CGRectGetWidth(frame), CGRectGetMinY(frame) + middleRatioPositionY * CGRectGetHeight(frame))];
[lineColor setStroke];
bezier3Path.lineWidth = lineWidth;
[bezier3Path stroke];
}// drawRect:
I've attached this source code with lines with CALayer and with UIBezierPath solutions. Touch on buttons and see the difference when they change.

Creating a triangle shape in a UIButton

I am trying to create a button with a triangle icon/shape in it. I have created the triangle in Paintcode then copied the beizer path and added it to the button layer as below.
-(void)setupShowHideRouteButton {
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
[bezierPath moveToPoint: CGPointMake(10.5, 8.5)];
[bezierPath addCurveToPoint: CGPointMake(40.5, 8.5) controlPoint1: CGPointMake(39.5, 8.5) controlPoint2: CGPointMake(40.5, 8.5)];
[bezierPath addLineToPoint: CGPointMake(26.39, 22.3)];
[bezierPath addLineToPoint: CGPointMake(25.2, 23.5)];
[bezierPath addLineToPoint: CGPointMake(10.5, 8.5)];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.frame = self.showHideRouteViewBtn.bounds;
shapeLayer.path = bezierPath.CGPath;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
shapeLayer.lineWidth = 2;
[self.showHideRouteViewBtn.layer addSublayer:shapeLayer];
}
However, this does not appear to work. I am setting the background color of the UIButton so I know the frame is correct and outlet working as expected it is just not adding the shape?
Second Approach
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
[bezierPath moveToPoint: CGPointMake(10.5, 8.5)];
[bezierPath addCurveToPoint: CGPointMake(40.5, 8.5) controlPoint1: CGPointMake(39.5, 8.5) controlPoint2: CGPointMake(40.5, 8.5)];
[bezierPath addLineToPoint: CGPointMake(26.39, 22.3)];
[bezierPath addLineToPoint: CGPointMake(25.2, 23.5)];
[bezierPath addLineToPoint: CGPointMake(10.5, 8.5)];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
// Create a mask layer and the frame to determine what will be visible in the view.
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
CGRect maskRect = self.showHideRouteViewBtn.bounds;
// Create a path with the rectangle in it.
CGPathRef path = CGPathCreateWithRect(maskRect, NULL);
// Set the path to the mask layer.
maskLayer.path = bezierPath.CGPath;
// Release the path since it's not covered by ARC.
CGPathRelease(path);
// Set the mask of the view.
self.showHideRouteViewBtn.layer.mask = maskLayer;
This did not work either.
Try the following:
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.frame = self.showHideRouteViewBtn.bounds;
shapeLayer.path = bezierPath.CGPath;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
shapeLayer.lineWidth = 2;
self.showHideRouteViewBtn.layer.mask = shapeLayer;

Animating CAShapeLayer to follow the lline of UIBezierPath

I an trying to animate the stroke of a UIBezierPath, as if drawing with a pencil.
However, I am finding that the layer seems to be creating an entire shape by connecting my first and last points on the curve -- and this is not what I want. I simply want a line of stroke 2.0 to animate. Here is my code:
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(20.0f, 20.f)];
[path addLineToPoint:CGPointMake(20.f, 200.f)];
CGFloat radius = 6.f;
[path addArcWithCenter:CGPointMake(20.f + radius, 200.f) radius:radius startAngle:M_PI endAngle:M_PI/2 clockwise:NO];
[path addLineToPoint:CGPointMake(240, 200.f+radius)];
[path addLineToPoint:CGPointMake(240.f, 480.f)];
[path stroke];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [path CGPath];
shapeLayer.strokeColor = [[UIColor redColor] CGColor];
shapeLayer.lineWidth = 2.0;
[self.scrollView.layer addSublayer:shapeLayer];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 4.f;
pathAnimation.fromValue = #(0.0f);
pathAnimation.toValue = #(1.0f);
pathAnimation.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.6 :0.3 :0.8 :0.45];
[shapeLayer addAnimation:pathAnimation forKey:#"strokeEnd"];
and here is what it looks like:

Process of animating paths using Quart2D

I am new to using Quartz2D, and to start small I wanted to draw a line, but have that line be animated from start to finish. From the blogs I have read and similar questions I have looked at, it seems I need to set a path for layer, and animate on that layer. The problem for me is, even though a layer has a path property, I am unsure how to go about properly settings up a path for it.
I have a UIView displaying, and it shows a line just fine if I comment out the animation code.
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGFloat components[] = {0.0, 0.0, 1.0, 1.0};
CGColorRef color = CGColorCreate(colorspace, components);
CGContextSetStrokeColorWithColor(context, color);
CGContextMoveToPoint(context, 0, 0);
CGContextAddLineToPoint(context, 300, 400);
CGContextStrokePath(context);
CGColorSpaceRelease(colorspace);
CGColorRelease(color);
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 10.0;
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
//[aLayer addAnimation:pathAnimation forKey:#"strokeEndAnimation"];
What do I need to do to animate a line from start to finish?
Here is what I've used to animate a line being drawn, make sure to import Quartz of course:
//Create the bezier path that will hold all the points of data
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[[UIColor greenColor] setFill];
[bezierPath fill];
[[UIColor redColor] setStroke];
[bezierPath stroke];
for (int i = 10 ; i < 20; i++) {
//If its the first point we need to make sure and move to that point not add to it
if (i == 10)
[bezierPath moveToPoint: CGPointMake(10, 10)];
else
[bezierPath addLineToPoint: CGPointMake(i * 2, i * 3)];
}
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.strokeColor = [[UIColor blueColor] CGColor];
shapeLayer.fillColor = nil;
shapeLayer.lineWidth = 10.0f;
shapeLayer.lineJoin = kCALineJoinRound;
shapeLayer.path = bezierPath.CGPath;
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 10.0;
pathAnimation.fromValue = #(0.0f);
pathAnimation.toValue = #(1.0f);
[shapeLayer addAnimation:pathAnimation forKey:#"strokeEnd"];
[self.view.layer addSublayer:shapeLayer];

Resources