Подтвердить что ты не робот

Анимация завершает обратный вызов для CALayer?

Мне интересно, где обратные вызовы (или если они есть) для анимаций в 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. Есть ли что-нибудь подобное?

ТИА.

4b9b3361

Ответ 1

Я ответил на свой вопрос. Вы должны добавить анимацию с помощью 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

Ответ 2

Вы можете использовать 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];

Ответ 3

Отработано 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];

Ответ 4

Вот ответ в 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() 

Ответ 5

Для 2018 года...

Не может быть проще.

Не забывайте [weak self], иначе вы упадете.

func animeExample() {

    CATransaction.begin()

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

    CATransaction.setCompletionBlock{ [weak self] in
        self?.animeExample()
        self?.ringBell()
        print("again...")
    }

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

,, ,, , ПРИМЕЧАНИЕ. КРИТИЧЕСКИЙ СОВЕТ>>>>>

Мы прерываем этот пост для критического совета:

ДОЛЖНА быть строка setCompletionBlock ДО строки someLayer.add.

Вы можете Google, чтобы прочитать более подробную информацию об этом, но в целом важно иметь его в таком порядке. А теперь вернемся к посту!

& Lt; & Lt; & Lt; & Lt; & Lt; ПРИМЕЧАНИЕ. - КРИТИЧЕСКИЙ СОВЕТ., ,,.

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

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


Примечания для любого новичка в iOS-анимации:

  1. "Ключ" (как в forKey) не имеет значения и редко используется. Установите это в ноль. Если вы хотите установить его, установите для него "любую строку".

  2. "KeyPath" - это фактически фактическая "вещь, которую вы анимируете". Это буквально свойство слоя, такое как "opacity", "backgroundColor" и т.д., Но записанное в виде строки. (Вы не можете просто ввести "все, что вы хотите" там, это должно быть имя фактического свойства слоя, и оно должно быть анимируемым.)

Повторим: "ключ" (редко используется - просто установите его в ноль) и "keyPath" совершенно не связаны друг с другом.

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


Обратите внимание, что поочередно вы можете использовать делегата, но гораздо проще просто использовать блок завершения, поскольку (A) он самодостаточен и может использоваться где угодно, и (B) у вас обычно есть несколько аниме, в этом случае с помощью делегат зануда

Ответ 6

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

Ответ 7

В 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")
  }

Ответ 8

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

Ответ 9

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

Ответ 10

Если вы не хотите возиться с CATransaction. Я создал расширение, которое инкапсулирует обратный вызов в объекте анимации.

Код можно найти в этом фрагменте: https://gitlab.com/snippets/1786298

И вы можете использовать его следующим образом:

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

Ответ 11

Кроме того, в моем случае в качестве делегата у меня был открытый класс UIView, а animationDidStop: Finish не был вызван. Я избавился от "публики" и теперь это работает. В случае, если некоторые из вас могут иметь эту проблему.