Bezier curve animation, as if the drawing continues on the iPhone screen

I want to draw the path of the bezel-free outline of the tomb of the Taj Mahal.

Q1. How can I draw it in Xcode? I could easily do this in PS, but it's hard for me to do in Xcode programmatically, especially by getting the coordinates of the control points.

Q2. Moreover, how can I fix the coordinates of the points when changing the screen size?

Q3. How could I revive it, as if an invisible pencil is drawing it on the screen?

+7
ios objective-c animation uibezierpath
source share
3 answers

How to draw a bezier path in UIKit ?

Create a UIBezierPath object, and then use various methods to create it .

I suspect the methods that interest you most are as follows:

For example, something like this will create a simple bezier path with a quadratic curve:

 UIBezierPath* yourPath = [UIBezierPath bezierPath]; // create the bezier path [yourPath moveToPoint:CGPointMake(100, 100)]; // move to your starting point [yourPath addQuadCurveToPoint:CGPointMake(300, 500) controlPoint:CGPointMake(500, 350)]; // add a new quad curve to the point 

However, if you're used to using something like Photoshop to create your own paths, you might be interested

You can add this UIBezierPath to the path property for CAShapeLayer to display it on the screen.

 CAShapeLayer* yourShapeLayer = [CAShapeLayer layer]; // create your shape layer yourShapeLayer.frame = CGRectMake(0, 0, 300, 500); // assign it frame yourShapeLayer.path = yourPath.CGPath; // add your path to the shape layer yourShapeLayer.fillColor = [UIColor clearColor].CGColor; // prevent the shape layer from filling [self.view.layer addSublayer:yourShapeLayer]; // add the shape layer to the view 

Then you can easily set the strokeColor and lineWidth at the form level to customize the way you stroke your path.

 yourShapeLayer.strokeColor = [UIColor redColor].CGColor; // red stroke color yourShapeLayer.lineWidth = 5.0; // 5 point stroke width 

You should get something like this:

How could I revive it, as if an invisible pencil is drawing it on the screen?

You can easily create a CABasicAnimation that can animate the strokeStart or strokeEnd property of the CAShapeLayer to achieve the desired animation.

This can lead to the effect of animating the line received on the screen.

For example, this code will animate the strokeEnd property from 0.0 to 1.0 , creating the desired effect:

 yourShapeLayer.strokeStart = 0.0; // reset stroke start before animating CABasicAnimation* strokeAnim = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; // create animation that changes the strokeEnd property strokeAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; // give it a nice timing function strokeAnim.duration = 2.0; // duration of the animation strokeAnim.fromValue = @(0.0); // provide the start value for the animation, wrapped in an NSNumber literal. strokeAnim.toValue = @(1.0); // provide the end value for the animation, wrapped in an NSNumber literal. [yourShapeLayer addAnimation:strokeAnim forKey:@"strokeAnim"]; // add animation to layer 

How can I scale this path to work with screens of different sizes?

I usually like to solve this problem: determine the waypoints in a given fixed frame (for example, 500 x 500 points), and then generate a scale factor based on the screen size (in your case, you want to scale to the screen width).

So all we need to do is generate our scale factor.

 CGFloat sf = self.view.frame.size.width/500.0; // The factor to scale the path by 

Then we just need to multiply all of our points in UIBezierPath by this scale factor. For example, using our previous path:

 UIBezierPath* yourPath = [UIBezierPath bezierPath]; // create the bezier path [yourPath moveToPoint:CGPointMake(100.0*sf, 100.0*sf)]; // move to your starting point [yourPath addQuadCurveToPoint:CGPointMake(300.0*sf, 500.0*sf) controlPoint:CGPointMake(500.0*sf, 350.0*sf)]; 

Now all we need to do is program at our points on the bezier path, as if we were always drawing it in a frame of size 500 x 500 (my points already fit into this well).

You will also want to resize your CAShapeLayer so that it is square and occupies the entire width of the screen.

 yourShapeLayer.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.width); 

Finally, you want to place CAShapeLayer in the center of the screen. You can do this easily by setting the position property of the layer in the center view.

 yourShapeLayer.position = self.view.center; 

Your path should now always scale to fit the width of the screen.

(I added a gray background to the layer to illustrate the scaling)

Full project (for your convenience):

+19
source share

I wrote about this in 2010: Animating a CGPath drawing with CAShapeLayer .

The bottom line is:

  • Create a UIBezierPath or CGPath with the shape you want to draw. To create a form, use the methods moveToPoint: addLineToPoint: addCurveToPoint:controlPoint1:controlPoint2: etc.
  • Create a CGShapeLayer and assign the path to the layer path property.
  • Add a layer to the view / level hierarchy (for example, in the view controller: [self.view.layer addSublayer:shapeLayer]; ). You must also give the layer the actual size and position ( frame ).
  • strokeEnd layer strokeEnd property from 0 to 1.
+2
source share

The answer sent by originaluser2 solves everything. You can also try this method if you want to resize the bezier path for different Dievice screens:

You can simply subclass UIView and configure its drawRect method:

  - (void)drawRect: (CGRect)frame { //// Bezier Drawing UIBezierPath* bezierPath = [UIBezierPath bezierPath]; [bezierPath moveToPoint: CGPointMake(CGRectGetMinX(frame) + 0.23370 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.65441 * CGRectGetHeight(frame))]; [bezierPath addCurveToPoint: CGPointMake(CGRectGetMinX(frame) + 0.49457 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.33088 * CGRectGetHeight(frame)) controlPoint1: CGPointMake(CGRectGetMinX(frame) + 0.23370 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.65441 * CGRectGetHeight(frame)) controlPoint2: CGPointMake(CGRectGetMinX(frame) + 0.35870 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.33088 * CGRectGetHeight(frame))]; [bezierPath addCurveToPoint: CGPointMake(CGRectGetMinX(frame) + 0.77717 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.65441 * CGRectGetHeight(frame)) controlPoint1: CGPointMake(CGRectGetMinX(frame) + 0.63043 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.33088 * CGRectGetHeight(frame)) controlPoint2: CGPointMake(CGRectGetMinX(frame) + 0.77717 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.65441 * CGRectGetHeight(frame))]; [UIColor.blackColor setStroke]; bezierPath.lineWidth = 1; [bezierPath stroke]; } 

50x50 frame:

enter image description here

100x100 frame:

enter image description here

frame 100x50

enter image description here

This draws a Bezier curve in a 50x50 frame if you send a 50x50 frame. Also, this code automatically changes depending on the size of the frame that it receives.

+1
source share

All Articles