How to animate a drawn line in Objective C? - objective-c

I am trying to animate drawn objects, for example I have drawn a line with this approach:
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(Center.x, Center.y)];
[path addLineToPoint:CGPointMake(Point.x,Point.y)];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [path CGPath];
shapeLayer.strokeColor = [[UIColor whiteColor] CGColor];
shapeLayer.lineWidth = 1.5;
shapeLayer.fillColor = [[UIColor clearColor] CGColor];
[self.view.layer addSublayer:shapeLayer];
After a while I need to animate the line to have a different Point, but keeping Center as is, but I have no idea how to do that. Is my approach alright? Is there a better way to do so?

Solved it:
CABasicAnimation *morph = [CABasicAnimation animationWithKeyPath:#"path"];
morph.duration = 1.0;
UIBezierPath *path5 = [UIBezierPath bezierPath];
[path5 moveToPoint:CGPointMake(P22.x+5, P22.y+5)];
[path5 addLineToPoint:CGPointMake(P21.x,P21.y)];
morph.fromValue = (id) path.CGPath;
morph.toValue = (id) path5.CGPath;
[shapeLayer addAnimation:morph forKey:nil];
//next line to update the shapeLayer's new path
shapeLayer.path = path5.CGPath;

Related

How to clip a triangular view from a rectangular view in objective C?

I want view like below image:-
desired view
I am using UIBezierPath and CAShapeLayer to archives this below code:-
UIBezierPath *path = [[UIBezierPath alloc] init];
// [[UIColor blackColor] setStroke];
// [[UIColor redColor] setFill];
[path moveToPoint:CGPointMake(_pendingView.frame.origin.x, _pendingView.frame.origin.y)];
[path addLineToPoint:CGPointMake(_pendingView.frame.origin.x+15, _pendingView.frame.origin.y+15)];
[path addLineToPoint:CGPointMake(_pendingView.frame.origin.x, _pendingView.frame.origin.y+30)];
[path closePath];
// [path fill ];
// [path stroke];
CAShapeLayer *shapeLayer = [CAShapeLayer new];
shapeLayer.frame = _pendingView.bounds;
shapeLayer.path = path.CGPath;
shapeLayer.fillColor = [UIColor redColor].CGColor;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
self.pendingView.layer.mask = shapeLayer;
// [self.pendingView.layer addSublayer:shapeLayer];
but i am unable to achieve. View appear without Clipping and background colour of view.
P.S :- pendingview is a view.
AS PER #inderJit suggestion, i rectify my code but i am getting this output image not desired one which above attached. View background colour causing problem. how can i achieve that??
In your code you are using _pendingView i.e. your UIView, origin for drawing path but layer will placed inside the _pendingView, so because of this you are not getting proper output.
For more clarification you can go through the below code
UIBezierPath *path = [[UIBezierPath alloc] init];
// [[UIColor blackColor] setStroke];
// [[UIColor redColor] setFill];
[path moveToPoint:CGPointMake(0, 0)];
[path addLineToPoint:CGPointMake(15, _vwArrow.frame.size.height/2)];
[path addLineToPoint:CGPointMake(0, _vwArrow.frame.size.height)];
[path closePath];
// [path fill ];
// [path stroke];
CAShapeLayer *shapeLayer = [CAShapeLayer new];
shapeLayer.frame = _vwArrow.bounds;
shapeLayer.path = path.CGPath;
shapeLayer.fillColor = self.view.backgroundColor.CGColor;
// shapeLayer.strokeColor = [UIColor blackColor].CGColor;
// _vwArrow.layer.masksToBounds = TRUE;
[_vwArrow.layer addSublayer:shapeLayer];
[self.view setNeedsDisplay];

Create UIView with four corner points?

How can i create UIView with four corner.
0 = "NSPoint: {79.583483072916664, 54.148963562011716}"; // top left
1 = "NSPoint: {274.96375, 30.877176879882814}"; // top right
2 = "NSPoint: {306.15565104166666, 357.91370518493653}"; // bottom right
3 = "NSPoint: {77.101464843749994, 363.47374218750002}"; // bottom left
I have no idea where to start.
As your view is not a regular rectangle you need to first draw a path i.e. BezierPath and get a shape and imposing the shape on to the View. This view myView would behave like any other normal view
UIView *myView = [[UIView alloc]initWithFrame:self.view.frame];
UIBezierPath *linePath = [UIBezierPath bezierPath];
[linePath moveToPoint:CGPointMake(points[0].x, points[0].y)];
[linePath addLineToPoint:CGPointMake(points[1].x, points[1].y)];
[linePath addLineToPoint:CGPointMake(points[2].x, points[2].y)];
[linePath addLineToPoint:CGPointMake(points[3].x, points[3].y)];
[linePath closePath];
CAShapeLayer *shape = [[CAShapeLayer alloc]init];
[shape setPath:linePath.CGPath];
myView.layer.mask=shape;
myView.backgroundColor = [UIColor redColor];
[self.view addSubview:myView];
The output ScreenShot is as given below:
Your 4 corner points do not make a rectangle - a UIView cannot be an arbitrary quadrilateral.
For drawing irregular shapes you should look at Apple's docs for using bezier paths: https://developer.apple.com/library/content/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/BezierPaths/BezierPaths.html
So something like this:
- (void)drawRect:(CGRect)rect {
[[UIColor redColor] setStroke];
[[UIColor yellowColor] setFill];
UIBezierPath *linePath = [UIBezierPath bezierPath];
[linePath moveToPoint:CGPointMake(points[0].x, points[0].y)];
[linePath addLineToPoint:CGPointMake(points[1].x, points[1].y)];
[linePath addLineToPoint:CGPointMake(points[2].x, points[2].y)];
[linePath addLineToPoint:CGPointMake(points[3].x, points[3].y)];
[linePath setLineWidth:LINE_WIDTH];
[linePath closePath];
}
What you want is polygon shape and not rectangle. You can not assign polygon shape to view. but if you want to display only part of view fall between these points, you can draw UIBezierPath path with these points on view and then apply blend to clip outer part. here is sample code.
self.backgroundColor = [UIColor redColor];
UIBezierPath *aPath = [UIBezierPath bezierPath];
[aPath moveToPoint:CGPointMake( a.x, a.y)];
[aPath addLineToPoint:CGPointMake(b.x, b.y)];
[aPath addLineToPoint:CGPointMake(c.x, c.y)];
[aPath addLineToPoint:CGPointMake( d.x, d.y)];
[aPath closePath];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = aPath.CGPath;
[self.layer setMask:shapeLayer];
You can use CALayer for corners, Below lines create a view with corners rounded by 8.
UIView *myView = [[UIView alloc] init];
myView.layer.masksToBounds = YES;
myView.layer.cornerRadius = 8.0;
Or you can create view with certain frame like
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(x, y, w, h)];

Making a gradient arc?

i wanna make an arc with the use of CAGradientLayer in such a way:-
20% of the arc have same single color then other 80% of arc has a gradient of two colors.
I already tried by hand on locations
startPoint endPoint property of CAGradientLayer but couldn't get the success and already go through from tutorials but somehow couldn't understand the concept properly.please solve my problem with clear description of it.
// for beizier path
- (UIBezierPath *)samplePath
{
UIBezierPath *path = [UIBezierPath bezierPath];
path = [UIBezierPath bezierPath];
[path addArcWithCenter:CGPointMake(200, 200) radius:30.0f startAngle:(3*M_PI)/4 endAngle:M_PI/4 clockwise:YES];
path.lineWidth = 30;
[[UIColor redColor] setStroke];
// [[UIColor colorWithRed:arc4random() green:arc4random() blue:arc4random() alpha:1.0] setFill];
[path stroke];
return path;
}
- (void)startAnimation
{
CAShapeLayer *shapeLayer;
if (self.pathLayer == nil)
{
shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [[self samplePath] CGPath];
shapeLayer.strokeColor = [[UIColor redColor] CGColor];
shapeLayer.fillColor = nil;
shapeLayer.lineWidth = 30;
self.pathLayer = shapeLayer;
}
[self animationBasic];
[self gradientLayer:shapeLayer];
}
-(void)animationBasic
{
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 3.0;
pathAnimation.fromValue = #(0.0f);
pathAnimation.toValue = #(1.0f);
[self.pathLayer addAnimation:pathAnimation forKey:#"strokeEnd"];
}
-(void)gradientLayer:(CAShapeLayer *)shapelayer
{
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = self.frame;
gradientLayer.colors = #[(__bridge id)[UIColor yellowColor].CGColor,(__bridge id)[UIColor redColor].CGColor ];
gradientLayer.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.5f],
[NSNumber numberWithFloat:1.0f],
nil];
// gradientLayer.startPoint = CGPointMake(0,1);
// gradientLayer.endPoint = CGPointMake(1,1);
[self.layer addSublayer:gradientLayer];
gradientLayer.mask = shapelayer;
}
The locations property of a CAGradientLayer instance should be populated with the starting points (I know, the word stops makes it sound the opposite) of each color as specified in the colors property, from 0 to 1. These stops are rendered vertically from top to bottom.
So, this should work:
gradientLayer.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:0.5f],
nil];
Now, at the same time, if you want to make the gradient horizontal (or any other direction), you can make use of the startPoint and endPoint properties.
gradientLayer.startPoint = CGPointMake(0.0, 0.5); //Default is (0.5, 0,0)
gradientLayer.endPoint = CGPointMake(1.0, 0.5); //Default is (0.5, 1.0)
//The gradientLayer will be rendered horizontally.
The startingPoint and endingPoint properties determine the direction and layout of the gradient in the layer. These are defined in the unit coordinate system, where each point is defined as a set of unit coordinates and the top left corner being the origin. So, (0,0) corresponds to the origin, (1,1) corresponds to the bottom right corner. You can check this excellent answer for a better explanation.
So to summarize, the gradient is rendered in the direction and region as specified by the startPoint and endPoint properties, with the colors rendered in the ratio specified in the locations property.
At the end, you can only understand this fully by tinkering with the properties and using various combinations to see what effect is rendered.

Animating NSView via NSBezierPath

I have some NSBezierPath, and some NSView. I want to animate this NSView and use NEBezierPath as a animation path.
Creating CAKeyframeAnimation with path from UIBezierPath on IOS works fine for me, but for OSX it's not exactly the same.
Here is the code:
self.headView = [[NSView alloc] initWithFrame:NSMakeRect(100, 100, 50, 50)];
[self.headView setWantsLayer:YES];
[self.headView.layer setBackgroundColor:[NSColor redColor].CGColor];
[self.contentView addSubview:self.headView];
NSBezierPath *path = [NSBezierPath bezierPath];
[path moveToPoint:pathStart];
[path curveToPoint:endPoint controlPoint1:[pointValue pointValue] controlPoint2:[pointValue pointValue]];
[path setLineWidth:3.0];
[[NSColor whiteColor] set];
[path stroke];
CAKeyframeAnimation *posAnim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
posAnim.path = [path quartsPath];
posAnim.duration = 1.0;
posAnim.calculationMode = kCAAnimationLinear;
[self.headView.layer addAnimation:posAnim forKey:#"posAnim"];
Converting NSBezierPath to CGPathRef is from there: https://developer.apple.com/library/mac/documentation/cocoa/Conceptual/CocoaDrawingGuide/Paths/Paths.html#//apple_ref/doc/uid/TP40003290-CH206-SW2
But it doesn't animate at all, here is the video: http://d.pr/v/glqQ
I don't getting what is the problem...
P.S. I prefer Keyframe animation because I would like to add some bouncing effect latter.
Make sure the superview of your headView also has a valid layer. Try this:
self.contentView.wantsLayer = YES;

Adding Bezier path to CGPath: ObjectiveC

I have a rectangle drawn using drawRect method.
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect outline = CGRectMake(4, 4, width - 8, height - 8);
CGColorRef white = [[UIColor whiteColor] CGColor];
CGColorRef black = [[UIColor blackColor] CGColor];
CGContextSetFillColorWithColor(context, white);
CGContextFillEllipseInRect(context, outline);
CGContextSetLineWidth(context, 2.0f);
CGContextSetStrokeColorWithColor(context, black);
CGContextStrokeEllipseInRect(context, outline);
I have a bezier path drawn in the samedrawn rect method.
[[UIColor blackColor] setStroke];
[[UIColor whiteColor] setFill];
UIBezierPath * path = [UIBezierPath bezierPath];
[path moveToPoint:point1];
[path addQuadCurveToPoint:point3 controlPoint:point2];
[path addQuadCurveToPoint:point5 controlPoint:point4];
[path setLineWidth:2.0f];
[path stroke];
[path fill];
I need to add a CALayer over these two shapes so that they appear as one.
aPath = CGPathCreateMutable();
CGPathAddEllipseInRect(aPath, nil, outline);
CGPathCloseSubpath(aPath);
pathCopy = CGPathCreateCopyByTransformingPath(aPath, nil);
myLayer = nil;
myLayer = [CAShapeLayer layer];
myLayer.path = pathCopy;
myLayer.fillColor = [[UIColor whiteColor] CGColor];
[self.layer addSublayer:myLayer];
The problem is that I cant add the bezier path to the CGPath. That's one.
Another one is that I cant add a border color or border width to the layer which I am creating. Can anyone help?
Try:
CGPathAddPath(aPath, nil, path.CGPath);
Where aPath is your mutable CGPath and path is your UIBezierPath.