CALayer的动画结束回调?


70

我想知道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中制作动画的目的。有没有类似的东西?

TIA。


对于任何在这里搜寻的人,我已经对这个难以置信的古老问题提出了现代答案!:O搜索到“ 2017 ...”
Fattie

Answers:


132

您可以使用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];

32
请注意,在将动画添加到图层之前设置完成块很重要。
2014年

1
这总是没有用的,因为即使删除动画时也将其删除,并且也会被调用。
Yuval Tal

66

我回答了我自己的问题。您必须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


3
这个答案对于有关委托的信息很有用,但是它有一个很大的缺陷-您不能为“框架”设置动画,因为它是派生的属性。developer.apple.com/library/mac/#qa/qa2008/qa1620.html
Michal,2010年

您无法编辑“框架”,但获取框架的中心并为其设置动画也很容易,例如-> letTheAnimation = CABasicAnimation(keyPath:“ position”); 然后获取框架中心点-> theAnimation.fromValue = NSValue(cgPoint:CGPoint(x:rightImage.frame.origin.x + rightImage.frame.width / 2.0,y:rightImage.frame.origin.y + rightImage .frame.height / 2.0))
亚当·弗里曼

请注意,这个10年的答案确实不是要走的路。(SO上的许多旧答案都是如此!)
Fattie

@Fattie-为什么会这样?我错过了任何弃用通知吗?
silverdr

54

这是基于bennythemink解决方案的Swift 3.0的答案:

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

52

对于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 BEFORE,行。

顺序很关键!这是一个iOS怪癖。

在示例中,它只是再次调用自身。

当然,您可以调用任何函数。


iOS动画新手注意事项:

  1. “键”(如中forKey无关紧要,很少使用。将其设置为nil。如果要设置,请将其设置为“任意字符串”。

  2. 实际上 “ keyPath”是实际的“动画对象”。从字面上看,它是图层的一个属性,例如“ opacity”,“ backgroundColor”等,但是写为string。(您不能只在此处输入“任何您想要的东西”,它必须是该图层的实际属性的名称,并且必须是可动画的。)

重复一下:“键”(很少使用-只需将其设置为nil)和“ keyPath”是完全无关的。

您经常会在示例代码中混淆了这两个代码(由于愚蠢的命名),这会导致各种问题。


请注意,您也可以使用委托,但是仅使用完成块要容易得多,因为(A)它是独立的并且可以在任何地方使用,并且(B)您通常拥有多个动画,在这种情况下使用代表很无聊。


很好的答案胖子–谢谢!您介意[weak self]详细解释该部分吗?对我来说,没有它也可以工作。
ixany19年

@ixany,您好-如果您不使用弱自我,则在动画过程中视图消失时它将崩溃。这是iOS工程的基本知识,您可以在此处阅读许多有关此内容的质量检查!请享用。
Fattie

50

用这个垃圾浪费了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];

我想,由于您的原因,您可能已经使用了autoreverse属性,因为您所做的基本上是在反转第一个动画。
denis631 '16

1
这个答案使我发笑!我已经完全被那里试图让一个看似很小的事情工作了几个小时......✊
杰森ž

他们说您不应该animation.removedOnCompletion = NO;在启动动画之前使用而是设置图层的值。这样,你搞乱了介绍和模型层..
马丁贝格

12

只是为那些在Google上找到此页面的人提供的一条注释:通过将动画对象的“ delegate”属性设置为将接收通知的对象,并在该对象的.m中实现“ animationDidStop”方法,您真的可以完成工作。文件。我只是试过了,而且有效。我不知道为什么乔·布洛说那不是正确的方法。


使用setCompletionBlock和CATransation.begin尝试了从removeAllAnimations到特定的CAAnimation的所有内容,并像user3077725的答案一样进行提交。尝试过UIView.animateWIthDuration。无法用于简单的alpha和position.x动画。委托方法是唯一可以正常使用的方法
KorinW

11

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

7

斯威夫特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()
}

这对我来说非常有效,但是我必须使用:Swift 5中的CATransaction.setCompletionBlock(completion)并确保:animation.fillMode = .forwards animation.isRemovedOnCompletion = false设置为确保链接在一起的所有动画链式物品没有破损。谢谢!
kirikintha

0

设置CAAnimation对象时,可以设置给定动画的名称。在animationDiStop:finished中,只需比较提供的Animation对象的名称即可根据动画执行特定的功能。


3
没有名称属性?
pronebird 2014年

0

对于2020年...

ValueAnimator,更新您的自定义属性。

https://github.com/Only-IceSoul/ios-jjvalueanimator

 class OnAnimationListener : AnimatorListener {

        weak var s : ViewController?

        init(_ ins: ViewController) {
            s = ins
        }
        func onAnimationStart(_ animation: Animator) {}
        func onAnimationEnd(_ animation: Animator) {

           print("end")
           s?.label.text = "end"

        }
        func onAnimationCancel(_ animation: Animator) {}
        func onAnimationRepeat(_ animation: Animator) {}

    }

0

我在CAAnimation上写了一个扩展,给您一个开始和完成的结束,因为我受够了实现委托,尤其是对于多个动画,您必须做一些可怕的事情,例如使用动画的键来查看哪个动画是呼叫代表-使这种事情变得非常容易。

在GitHub上-动画动作

希望对某人有用!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.