2012-11-14 19 views
12

ich einen Kreis am zeichnen, mit einem anfänglichen Radius von 200Animieren CAShapeLayer Größenänderung

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) 

Kann jemand bitte sagen Sie mir, wie eine Änderung auf einen Radius von 100 zu animieren?

+0

Animiert es nicht, wenn Sie die Grenzen in einem Animationsblock ändern? – Lefteris

+0

@ Jonathan Funktioniert meine Antwort nicht? –

+0

@Lefteris Leider nicht. Ich mache es vielleicht falsch, aber der Kreis zeichnet nur neu. – Jonathan

Antwort

21

Dies ist, wie ich es tun endete:

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]; 

Dank Jacob für den Hinweis mich in die richtige Richtung.

+0

Markieren Sie diese Antwort als Antwort. – jonsibley

+0

Sie sollten weder removedOnCompletion noch fillMode verwenden. Sie sollten tun: self.circle = newPath.CGPath; [self.circle addAnimation: ...]. Auf diese Weise ist der Pfad korrekt, wenn die Animation endet UND Sie kein Animationsobjekt verlieren. – nevyn

+0

Ich habe diesen Ansatz schon einmal benutzt und manchmal entstehen Speicherlecks.Sie machen die Animation möglich, ändern aber nicht den zugrunde liegenden Wert von self.circle.path auf eine dauerhafte Weise ... Sie lassen nur eine nicht entfernte Animation angehängt, die den Pfad so aussehen lässt, als wäre er permanent geändert worden. Siehe meine andere Antwort für einen Ansatz, der sauberer funktioniert (und der Dokumentation von Apple folgt). Ich habe lange damit gerungen, aber jetzt verstehe ich, wie es funktioniert! –

2

können Sie einen CAAnimationGroup verwenden, um die bounds und path Ihrer animieren CAShapeLayer:

- (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]; 
} 
+0

Danke Jacob, aber leider funktioniert das nicht. Der Kreis bleibt gleich groß und bewegt sich nur in der Position. – Jonathan

2

Hier ist eine direkte schnelle Umwandlung von Jonathans Code sollte jemand es brauchen.

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); 
} 
6

Das ist, glaube ich, die einfachere und bevorzugte Art und Weise, es zu tun:

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; 

ich diesen Ansatz bekam von der Apple Documentation here (siehe Listing 02.03). Die wichtigen Dinge hier sind, dass Sie die setzen und auch den endgültigen Wert für self.circlepath zu newPath.CGPath direkt nach dem Einrichten der Animation festlegen. Eine Animation ändert den zugrunde liegenden Wert des Modells nicht. In der anderen Vorgehensweise ändern Sie also nicht permanent die path Variable der Shape-Ebene.

Ich habe eine Lösung wie die gewählte Antwort in der Vergangenheit (mit removedOnCompletion und fillMode etc) verwendet, aber ich habe festgestellt, dass auf bestimmten Versionen von iOS Speicherverluste verursacht, und auch manchmal dieser Ansatz funktioniert einfach nicht für einige Grund.

Mit diesem Ansatz ist die Animation explizit zu schaffen (beide wo es beginnt und wo es endet), und Sie am Ende den endgültige bleibenden Wert für den Pfad angeben (mit der letzten Zeile) , so dass es keine Notwendigkeit gibt, sich damit zu befassen, die Animation nicht zu entfernen, wenn sie beendet ist (was meiner Meinung nach zu Speicherverlusten führt, wenn Sie diese Animation oft machen ... die nicht entfernten Animationen werden im Speicher aufgebaut).

Auch habe ich die Animation der bounds entfernt, weil Sie es nicht brauchen, um den Kreis zu animieren. Auch wenn der Pfad außerhalb der Grenzen der Ebene gezeichnet wird, wird er immer noch vollständig angezeigt (Informationen hierzu finden Sie in der Dokumentation für CAShapeLayer). Wenn Sie die Grenzen aus einem anderen Grund animieren müssen, können Sie denselben Ansatz verwenden (stellen Sie sicher, dass Sie den Wert und den Endwert für die Grenzen angeben).

+0

Diese Animation verursacht Warping, wenn ich es versuchte. Hier sind einige [Ernten] (https://imgur.com/a/H8kk4) der laufenden Animation. (Wächst von klein bis groß). Nach dem Ende ist es normal. – Micrified

Verwandte Themen