2012-11-14 25 views
12

jestem rysowanie okręgu, z początkowym promieniu 200Animowanie CAShapeLayer zmiana rozmiaru

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) 

Czy ktoś może mi powiedzieć jak do animowania zmianę promieniu 100?

+0

Czy to nie jest animacja, jeśli modyfikujesz granice w bloku animacji? – Lefteris

+0

@ Jonathan Czy moja odpowiedź nie działa? –

+0

@Lefteris Niestety nie. Być może robię to źle, ale krąg po prostu przerysowuje. – Jonathan

Odpowiedz

21

ten sposób skończyło się to robi:

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

Dzięki Jakubowi za wskazanie mi w dobry kierunek.

+0

Zaznacz tę odpowiedź jako odpowiedź. – jonsibley

+0

Nie należy używać metody removeOnCompletion ani fillMode. Powinieneś zrobić: self.circle = newPath.CGPath; [self.circle addAnimation: ...]. W ten sposób ścieżka będzie prawidłowa, gdy animacja się zakończy, ORAZ nie wyciekniesz z obiektu animacji. – nevyn

+0

Używałem już tego podejścia i czasami tworzy wycieki pamięci.Tworzysz animację, ale nie zmieniasz podstawowej wartości self.circle.path na stałe ... po prostu pozostawiasz nieusuniętą animację, która sprawia, że ​​ścieżka wygląda tak, jakby została na stałe zmieniona. Zobacz moją drugą odpowiedź na podejście, które działa bardziej czysto (i postępuje zgodnie z dokumentacją Apple). Walczyłem z tym przez długi czas, ale w końcu rozumiem, jak to działa teraz! –

2

Można użyć CAAnimationGroup animować bounds i path twoich 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

Dzięki Jacob, ale niestety to nie działa. Koło pozostaje tej samej wielkości i po prostu przesuwa pozycję. – Jonathan

2

Oto bezpośrednia, szybka konwersja kodu Jonathana, jeśli ktoś go potrzebuje.

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

To jest, moim zdaniem, prostsze i preferowany sposób to zrobić:

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; 

mam takie podejście z Apple Documentation here (patrz Listing 3-2). Ważne jest, aby ustawić fromValue i ustawić końcową wartość dla self.circle z path na newPath.CGPath zaraz po ustawieniu animacji. Animacja nie zmienia podstawowej wartości modelu, więc w innym podejściu nie wprowadzasz trwałej zmiany w zmiennej path warstwy kształtu.

Użyłem rozwiązania takiego jak wybrana odpowiedź w przeszłości (używając removedOnCompletion i fillMode itp.), Ale odkryłem, że w niektórych wersjach iOS powodowały wycieki pamięci, a także czasami takie podejście po prostu nie działa dla niektórych powód.

Dzięki takiemu podejściu tworzysz animację jawnie (zarówno od miejsca, w którym zaczyna się i gdzie się kończy), i określasz końcową stałą wartość ścieżki na końcu (z tą ostatnią linią) , więc nie ma potrzeby zadzieraj z usunięciem animacji po jej zakończeniu (co, jak uważam, prowadzi do przecieków pamięci, jeśli wykonujesz tę animację wiele razy ... nieusunięte animacje gromadzą się w pamięci).

Usunąłem również animację z bounds, ponieważ nie jest ona potrzebna do animowania okręgu. Nawet jeśli ścieżka zostanie narysowana poza granicami warstwy, nadal będzie w pełni widoczna (zobacz dokumentację na ten temat w dokumencie CAShapeLayer). Jeśli potrzebujesz animować granice z jakiegoś innego powodu, możesz użyć tego samego podejścia (upewnij się, że podałeś fromValue i ostateczną wartość dla granic).

+0

Ta animacja powoduje wypaczenia, gdy próbowałem. Oto niektóre [uprawy] (https://imgur.com/a/H8kk4) animacji w toku. (Rośnie od małej do dużej). Po jego zakończeniu jest to normalne. – Micrified