动画CAShapeLayer大小更改

我正在绘制一个圆,初始半径为200

self.circle = [CAShapeLayer layer]; self.circle.fillColor = nil; self.circle.strokeColor = [UIColor blackColor].CGColor; self.circle.lineWidth = 7; self.circle.bounds = CGRectMake(0, 0, 2 * radius, 2 * radius); self.circle.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.radius, self.radius) 

任何人都可以告诉我如何将更改设置为半径100的动画?

这就是我最终做到的方式:

 UIBezierPath *newPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(newRadius, newRadius) radius:newRadius startAngle:(-M_PI/2) endAngle:(3*M_PI/2) clockwise:YES]; CGRect newBounds = CGRectMake(0, 0, 2 * newRadius, 2 * newRadius); CABasicAnimation* pathAnim = [CABasicAnimation animationWithKeyPath: @"path"]; pathAnim.toValue = (id)newPath.CGPath; CABasicAnimation* boundsAnim = [CABasicAnimation animationWithKeyPath: @"bounds"]; boundsAnim.toValue = [NSValue valueWithCGRect:newBounds]; CAAnimationGroup *anims = [CAAnimationGroup animation]; anims.animations = [NSArray arrayWithObjects:pathAnim, boundsAnim, nil]; anims.removedOnCompletion = NO; anims.duration = 2.0f; anims.fillMode = kCAFillModeForwards; [self.circle addAnimation:anims forKey:nil]; 

感谢雅各布指出我正确的方向。

我认为,这是更简单和首选的方法:

 UIBezierPath *newPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(newRadius, newRadius) radius:newRadius startAngle:(-M_PI/2) endAngle:(3*M_PI/2) clockwise:YES]; CABasicAnimation* pathAnim = [CABasicAnimation animationWithKeyPath:@"path"]; pathAnim.fromValue = (id)self.circle.path; pathAnim.toValue = (id)newPath.CGPath; pathAnim.duration = 2.0f; [self.circle addAnimation:pathAnim forKey:@"animateRadius"]; self.circle.path = newPath.CGPath; 

我从Apple文档中获得了这种方法(参见清单3-2)。 这里重要的是你设置了fromValue并在设置动画后立即将newPath.CGPathpath的最终值设置为self.circle 。 动画不会更改模型的基础值,因此在另一种方法中,您实际上并未对形状图层的path变量进行永久性更改。

我在过去使用了一个类似于所选答案的解决方案(使用removedOnCompletionfillMode等),但我发现在某些版本的iOS上它们会导致内存泄漏,有时这种方法也不会因某些原因而起作用。

使用这种方法,您可以显式创建动画(从它开始和结束的位置), 并且您在末尾指定路径的最终永久值(使用最后一行),因此不需要在完成动画时不要删除动画(我相信如果你多次执行这个动画会导致内存泄漏……未经删除的动画会在内存中积累)。

另外,我删除了bounds的动画,因为你不需要它来为圆圈设置动画。 即使路径被绘制到图层的边界之外,它仍然会被完全显示(请参阅CAShapeLayer的文档)。 如果由于某些其他原因确实需要为边界设置动画,则可以使用相同的方法(确保指定fromValue和边界的最终值)。

您可以使用CAAnimationGroupCAShapeLayerboundspath设置动画:

 - (void)animateToRadius:(CGFloat)newRadius { CAShapeLayer *shapeLayer = ...; UIBezierPath *newBezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(newRadius, newRadius)]; NSValue *newBounds = [NSValue valueWithCGRect:CGRectMake(0,0,2*newRadius,2*newRadius); CAAnimationGroup *group = [CAAnimationGroup animation]; NSMutableArray *animations = [@[] mutableCopy]; NSDictionary *animationData = @{@"path":newBezierPath.CGPath, @"bounds":newBounds}; for(NSString *keyPath in animationData) { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:keyPath]; animation.toValue = animationData[keyPath]; [animations addObject:animation]; } group.animations = [animations copy]; group.duration = 2; group.removedOnCompletion = NO; group.fillMode = kCAFillModeForwards; [shapeLayer addAnimation:group forKey:nil]; } 

如果有人需要,这是Jonathan代码的直接快速转换。

 func scaleShape(shape : CAShapeLayer){ let newRadius : CGFloat = 50; let newPath: UIBezierPath = UIBezierPath(arcCenter: CGPointMake(newRadius, newRadius), radius: newRadius, startAngle: (CGFloat(-M_PI) / 2.0), endAngle: (3 * CGFloat(M_PI) / 2), clockwise: true); let newBounds : CGRect = CGRect(x: 0, y: 0, width: 2 * newRadius, height: 2 * newRadius); let pathAnim : CABasicAnimation = CABasicAnimation(keyPath: "path"); pathAnim.toValue = newPath.CGPath; let boundsAnim : CABasicAnimation = CABasicAnimation(keyPath: "bounds"); boundsAnim.toValue = NSValue(CGRect: newBounds); let anims: CAAnimationGroup = CAAnimationGroup(); anims.animations = [pathAnim, boundsAnim]; anims.removedOnCompletion = false; anims.duration = 2.0; anims.fillMode = kCAFillModeForwards; shape.addAnimation(anims, forKey: nil); }