How to animate CoreGraphics drawing a form using CAKeyframeAnimation

I am trying to animate a UIBeizerPath drawing (in my example, a triangle) in a subclass of UIView. However, the entire subview is animated instead of the form.

Is there something that I am missing in the animation?

- (void)drawRect:(CGRect)rect { CAShapeLayer *drawLayer = [CAShapeLayer layer]; drawLayer.frame = CGRectMake(0, 0, 100, 100); drawLayer.strokeColor = [UIColor greenColor].CGColor; drawLayer.lineWidth = 4.0; [self.layer addSublayer:drawLayer]; UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointMake(0,0)]; [path addLineToPoint:CGPointMake(50,100)]; [path addLineToPoint:CGPointMake(100,0)]; [path closePath]; CGPoint center = [self convertPoint:self.center fromView:nil]; [path applyTransform:CGAffineTransformMakeTranslation(center.x, center.y)]; [[UIColor redColor] set]; [path fill]; [[UIColor blueColor] setStroke]; path.lineWidth = 3.0f; [path stroke]; CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; pathAnimation.duration = 4.0f; pathAnimation.path = path.CGPath; pathAnimation.calculationMode = kCAAnimationLinear; [drawLayer addAnimation:pathAnimation forKey:@"position"]; } 
+7
source share
1 answer
  • You create a CAShapeLayer , but then do nothing useful with it. Let me fix it.

  • Do not set layers and animations in -drawRect: because it strictly means drawing time using the CoreGraphics or UIKit APIs. Instead, you want CAShapeLayer to draw a triangle - this way you can animate it.

  • CAKeyframeAnimation.path is for something completely different (like moving a layer along a path).

  • Your animation animates the position value of this layer. No wonder he moves the layer! You want to animate the value of path .

  • The idea behind CAKeyframeAnimation is that you provide it with an array of values to set the layer property. During the time between keyframes, it will interpolate between two adjacent keyframes. Therefore, you need to specify several paths: one for each side.

  • Interpolating arbitrary paths is difficult. CA path interpolation works best when paths have the same number and type of elements. So, we are convinced that all our paths have the same structure, with only some points on each other.

  • The secret to animation, and possibly to computers in general: you have to be accurate in explaining what you want. "I want to animate the drawing of each point, so it seems to be animated," that's almost not enough information.

Here is a subclass of UIView that I think does what you ask for, or at least close. To revive, press the button before the -animate: action.

SPAnimatedShapeView.h:

 #import <UIKit/UIKit.h> @interface SPAnimatedShapeView : UIView - (IBAction)animate:(id)sender; @end 

SPAnimatedShapeView.m:

 #import "SPAnimatedShapeView.h" #import <QuartzCore/QuartzCore.h> @interface SPAnimatedShapeView () @property (nonatomic, retain) CAShapeLayer* shapeLayer; @end @implementation SPAnimatedShapeView @synthesize shapeLayer = _shapeLayer; - (void)dealloc { [_shapeLayer release]; [super dealloc]; } - (void)layoutSubviews { if (!self.shapeLayer) { self.shapeLayer = [[[CAShapeLayer alloc] init] autorelease]; self.shapeLayer.bounds = CGRectMake(0, 0, 100, 100); // layer is 100x100 in size self.shapeLayer.position = self.center; // and is centered in the view self.shapeLayer.strokeColor = [UIColor blueColor].CGColor; self.shapeLayer.fillColor = [UIColor redColor].CGColor; self.shapeLayer.lineWidth = 3.f; [self.layer addSublayer:self.shapeLayer]; } } - (IBAction)animate:(id)sender { UIBezierPath* path0 = [UIBezierPath bezierPath]; [path0 moveToPoint:CGPointZero]; [path0 addLineToPoint:CGPointZero]; [path0 addLineToPoint:CGPointZero]; [path0 addLineToPoint:CGPointZero]; UIBezierPath* path1 = [UIBezierPath bezierPath]; [path1 moveToPoint:CGPointZero]; [path1 addLineToPoint:CGPointMake(50,100)]; [path1 addLineToPoint:CGPointMake(50,100)]; [path1 addLineToPoint:CGPointMake(50,100)]; UIBezierPath* path2 = [UIBezierPath bezierPath]; [path2 moveToPoint:CGPointZero]; [path2 addLineToPoint:CGPointMake(50,100)]; [path2 addLineToPoint:CGPointMake(100,0)]; [path2 addLineToPoint:CGPointMake(100,0)]; UIBezierPath* path3 = [UIBezierPath bezierPath]; [path3 moveToPoint:CGPointZero]; [path3 addLineToPoint:CGPointMake(50,100)]; [path3 addLineToPoint:CGPointMake(100,0)]; [path3 addLineToPoint:CGPointZero]; CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath:@"path"]; animation.duration = 4.0f; animation.values = [NSArray arrayWithObjects:(id)path0.CGPath, (id)path1.CGPath, (id)path2.CGPath, (id)path3.CGPath, nil]; [self.shapeLayer addAnimation:animation forKey:nil]; } @end 
+17
source

All Articles