web-dev-qa-db-fra.com

Fin de l'animation pour CALayer?

Je me demande où sont les rappels (ou s'il y a quelque chose) pour les animations dans un CALayer. Plus précisément, pour les animations implicites telles que la modification du cadre, de la position, etc.

[UIView beginAnimations:@"SlideOut" context:nil];
[UIView setAnimationDuration:.3];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animateOut:finished:context:)];
CGRect frame = self.frame;
frame.Origin.y = 480;
self.frame = frame;
[UIView commitAnimations];

Plus précisément, la setAnimationDidStopSelector est ce que je veux pour une animation dans un CALayer. Y a-t-il quelque chose comme ça?

TIA.

59
Jeffrey Forbes

J'ai répondu à ma propre question. Vous devez ajouter une animation en utilisant CABasicAnimation comme ceci:

CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"frame"];
anim.fromValue = [NSValue valueWithCGRect:layer.frame];
anim.toValue = [NSValue valueWithCGRect:frame];
anim.delegate = self;
[layer addAnimation:anim forKey:@"frame"];

Et implémentez la méthode déléguée animationDidStop:finished: et vous devriez être prêt à partir. Dieu merci, cette fonctionnalité existe! :RÉ

56
Jeffrey Forbes

Vous pouvez utiliser une transaction CATransaction, elle dispose d’un gestionnaire de bloc d’achèvement.

[CATransaction begin];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
[pathAnimation setDuration:1];
[pathAnimation setFromValue:[NSNumber numberWithFloat:0.0f]];    
[pathAnimation setToValue:[NSNumber numberWithFloat:1.0f]];
[CATransaction setCompletionBlock:^{_lastPoint = _currentPoint; _currentPoint = CGPointMake(_lastPoint.x + _wormStepHorizontalValue, _wormStepVerticalValue);}];
[_pathLayer addAnimation:pathAnimation forKey:@"strokeEnd"];
[CATransaction commit];
116
bennythemink

Gâché 4 heures avec cette poubelle, juste pour faire un fondu en fondu. Notez le commentaire dans le code.

   [CATransaction begin];
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    animation.duration = 0.3;
    animation.fromValue = [NSNumber numberWithFloat:0.0f];
    animation.toValue = [NSNumber numberWithFloat:1.0f];
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeBoth;
  ///  [box addAnimation:animation forKey:@"j"]; Animation will not work if added here. Need to add this only after the completion block.

    [CATransaction setCompletionBlock:^{

        CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:@"opacity"];
        animation2.duration = 0.3;
        animation2.beginTime = CACurrentMediaTime()+1;
        animation2.fromValue = [NSNumber numberWithFloat:1.0f];
        animation2.toValue = [NSNumber numberWithFloat:0.0f];
        animation2.removedOnCompletion = NO;
        animation2.fillMode = kCAFillModeBoth;
        [box addAnimation:animation2 forKey:@"k"];

    }];

    [box addAnimation:animation forKey:@"j"];

    [CATransaction commit];
47
user3077725

Voici une réponse dans Swift 3.0 basée sur la solution de bennythemink:

    // Begin the transaction
    CATransaction.begin()
    let animation = CABasicAnimation(keyPath: "strokeEnd")
    animation.duration = duration //duration is the number of seconds
    animation.fromValue = 0
    animation.toValue = 1
    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    circleLayer.strokeEnd = 1.0

    // Callback function
    CATransaction.setCompletionBlock { 
        print("end animation")
    }

    // Do the actual animation and commit the transaction
    circleLayer.add(animation, forKey: "animateCircle")
    CATransaction.commit() 
43
tdon

Pour 2018 ...

Swift 4.

utiliser .setCompletionBlock

En pratique, vous avez besoin de [weak self] ou vous allez tomber en panne.

func animeExample() {

    CATransaction.begin()

    let a = CABasicAnimation(keyPath: "fillColor")
    a.fromValue, duration = ... etc etc

    CATransaction.setCompletionBlock{ [weak self] in
        self?.animeExample()
    }

    someLayer.add(a, forKey: nil)
    CATransaction.commit()
}

Dans l'exemple, il appelle simplement à nouveau.

Bien sûr, vous pouvez appeler n’importe quelle fonction.


Remarque: si vous commencez tout juste. Il faut se rappeler que 

  1. la "clé" (comme dans add#forKey) n'est pas pertinente et est rarement utilisée. Réglez-le à zéro. Si, pour une raison quelconque, vous souhaitez le définir, définissez-le sur "n'importe quelle chaîne" (par exemple, votre surnom). D'autre part...

  2. La keyPath dans l'appel CABasicAnimation est en fait la "chose que vous animez", autrement dit, il s'agit littéralement d'une propriété du calque (mais elle est simplement écrite sous forme de chaîne).

En bref, add#forKey est presque toujours nul, ce n'est pas pertinent. C'est totalement, complètement, sans rapport avec "keyPath" - le fait qu'ils aient tous les deux "key" dans le nom est une pure coïncidence, les deux choses sont totalement indépendantes.

Vous voyez souvent du code où ces deux sont confondus (grâce à la désignation idiote), ce qui cause toutes sortes de problèmes.


Notez que depuis peu, vous pouvez utiliser animationDidStop avec le délégué, voir la réponse par @jack ci-dessous! Dans certains cas, c'est plus facile. Parfois, il est plus facile de simplement utiliser un bloc d'achèvement. Si vous avez beaucoup d'animes différents (ce qui est souvent le cas), utilisez simplement des blocs de complétion.

35
Fattie

Juste une remarque pour ceux qui trouvent cette page sur Google: Vous pouvez vraiment faire le travail en définissant la propriété "delegate" de votre objet animation sur l'objet qui recevra la notification et en implémentant la méthode "animationDidStop" dans le fichier .m de cet objet. fichier. Je viens d'essayer, et ça marche. Je ne sais pas pourquoi Joe Blow a dit que ce n'était pas la bonne façon.

10
moonman239

Dans Swift 4+ je viens d'ajouter delegate en tant que 

class CircleView: UIView,CAAnimationDelegate {
...

let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.delegate = self//Set delegate

Rappel de fin d'animation -

func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
     print("Animation END")
  }
9
Jack

Swift 5.0

func blinkShadow(completion: @escaping (() -> Void)) {
    CATransaction.begin()
    let animation = CABasicAnimation(keyPath: "shadowRadius")
    animation.fromValue = layer.shadowRadius
    animation.toValue = 0.0
    animation.duration = 0.1
    animation.autoreverses = true
    CATransaction.setCompletionBlock(completion)
    layer.add(animation, forKey: nil)
    CATransaction.commit()
}
0
Jimmy_m

Si vous ne voulez pas perdre votre temps avec CATransaction. J'ai créé une extension qui encapsule le rappel dans l'objet d'animation.

Le code peut être trouvé dans cet extrait: https://gitlab.com/snippets/1786298

Et vous pouvez ensuite l'utiliser comme ceci: 

let appearAnimation = CASpringAnimation(keyPath: "transform")
appearAnimation.fromValue = contentView.layer.transform
appearAnimation.toValue = CATransform3DIdentity
appearAnimation.mass = 0.65
appearAnimation.duration = appearAnimation.settlingDuration
appearAnimation.isAdditive = true
appearAnimation.onCompletion {
    completed()
}
0
netigger

Vous pouvez définir le nom d'une animation donnée lors de la configuration de l'objet CAAnimation. Dans animationDiStop: terminé, il vous suffit de comparer le nom de l'objet Animation fourni pour exécuter une fonctionnalité spécifique en fonction de l'animation.

0
JavaJoe