Анимация завершения обратного вызова для CALayer? - PullRequest
62 голосов
/ 18 ноября 2008

Мне интересно, где находятся обратные вызовы (или есть ли что-нибудь) для анимации в CALayer. В частности, для неявных анимаций, таких как изменение кадра, положения и т. Д. В UIView вы можете сделать что-то вроде этого:

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

В частности, setAnimationDidStopSelector - это то, что я хочу для анимации в CALayer. Есть что-нибудь подобное?

ТИА.

Ответы [ 9 ]

119 голосов
/ 30 июня 2012

Вы можете использовать CATransaction, у него есть обработчик блока завершения.

[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];
56 голосов
/ 18 ноября 2008

Я ответил на свой вопрос. Вы должны добавить анимацию, используя CABasicAnimation примерно так:

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

И внедрите метод делегата animationDidStop:finished:, и все должно быть хорошо. Слава богу, этот функционал существует! : D

49 голосов
/ 30 октября 2014

Потратил впустую 4 часа с этим мусором, просто чтобы постепенно исчезнуть. Обратите внимание на комментарий в коде.

   [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];
44 голосов
/ 24 сентября 2016

Вот ответ в Swift 3.0, основанный на решении 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() 
38 голосов
/ 28 ноября 2017

за 2018 год ...

Swift 4.

использовать .setCompletionBlock

На практике вам нужно [weak self], или вы, как правило, вылетаете.

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()
}

В этом примере он просто снова вызывает себя.

Конечно, вы можете вызывать любую функцию.


Примечание: если вы только начинаете. Стоит помнить, что

  1. «ключ» (как в add#forKey) не имеет значения и используется редко. Установите это в ноль. Если по какой-либо причине вы хотите установить его, установите для него «любую строку» (скажем, ваш никнейм). С другой стороны ...

  2. keyPath в вызове CABasicAnimation на самом деле является фактической «вещью, которую вы анимируете», другими словами, это буквально свойство слоя (но просто записанное в виде строки).

Короче говоря add#forKey почти всегда просто ноль, это не имеет значения. Он полностью, совершенно не связан с «keyPath» - тот факт, что оба они имеют «ключ» в названии, является чистым совпадением, эти две вещи совершенно не связаны.

Вы часто видите код, в котором эти два понятия перепутаны (благодаря глупому именованию), что вызывает всевозможные проблемы.


Обратите внимание, что в последнее время вы можете использовать animationDidStop с делегатом, см. Ответ по @jack ниже! В некоторых случаях это проще; иногда проще просто использовать блок завершения. Если у вас много разных аниме (что часто бывает), просто используйте блоки завершения.

10 голосов
/ 02 января 2018

In Swift 4 + Я только что добавил delegate как

class CircleView: UIView,CAAnimationDelegate {
...

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

Обратный вызов завершения анимации -

func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
     print("Animation END")
  }
10 голосов
/ 26 сентября 2014

Просто примечание для тех, кто находит эту страницу в Google: вы действительно можете выполнить работу, установив свойство «делегировать» вашего объекта анимации для объекта, который будет получать уведомление, и реализовав в этом метод animationDidStop. .m файл объекта. Я только что попробовал, и это работает. Я не знаю, почему Джо Блоу сказал, что это не правильно.

1 голос
/ 11 апреля 2019

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 голосов
/ 19 ноября 2009

Вы можете установить имя данной анимации при настройке объекта CAAnimation. В animationDiStop: закончено, просто сравните имя объекта Animation, предоставленного для выполнения определенных функций на основе анимации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...