代码之家  ›  专栏  ›  技术社区  ›  Batgar

如何在AnimationDidStop代理中标识CAAnimation?

  •  95
  • Batgar  · 技术社区  · 16 年前

    我有一个问题,我有一系列重叠的cattransition/caanimation序列,所有这些都需要在动画停止时执行自定义操作,但我只需要一个用于animationdIdStop的委托处理程序。

    但是,我遇到了一个问题,似乎没有一种方法可以唯一地标识AnimationDidStop委托中的每个catTransition/caAnimation。

    我通过作为CAAnimation一部分公开的密钥/值系统解决了这个问题。

    启动动画时,使用catTransition/caAnimation上的setValue方法设置要在AnimationDidStop激发时使用的标识符和值:

    -(void)volumeControlFadeToOrange
    {   
        CATransition* volumeControlAnimation = [CATransition animation];
        [volumeControlAnimation setType:kCATransitionFade];
        [volumeControlAnimation setSubtype:kCATransitionFromTop];
        [volumeControlAnimation setDelegate:self];
        [volumeControlLevel setBackgroundImage:[UIImage imageNamed:@"SpecialVolume1.png"] forState:UIControlStateNormal];
        volumeControlLevel.enabled = true;
        [volumeControlAnimation setDuration:0.7];
        [volumeControlAnimation setValue:@"Special1" forKey:@"MyAnimationType"];
        [[volumeControlLevel layer] addAnimation:volumeControlAnimation forKey:nil];    
    }
    
    - (void)throbUp
    {
        doThrobUp = true;
    
        CATransition *animation = [CATransition animation]; 
        [animation setType:kCATransitionFade];
        [animation setSubtype:kCATransitionFromTop];
        [animation setDelegate:self];
        [hearingAidHalo setBackgroundImage:[UIImage imageNamed:@"m13_grayglow.png"] forState:UIControlStateNormal];
        [animation setDuration:2.0];
        [animation setValue:@"Throb" forKey:@"MyAnimationType"];
        [[hearingAidHalo layer] addAnimation:animation forKey:nil];
    }
    

    在您的AnimationDidStop代理中:

    - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag{
    
        NSString* value = [theAnimation valueForKey:@"MyAnimationType"];
        if ([value isEqualToString:@"Throb"])
        {
           //... Your code here ...
           return;
        }
    
    
        if ([value isEqualToString:@"Special1"])
        {
           //... Your code here ...
           return;
        }
    
        //Add any future keyed animation operations when the animations are stopped.
     }
    

    另一方面,它允许您在键值配对系统中保持状态,而不必将其存储在委托类中。代码越少越好。

    一定要查看 Apple Reference on Key Value Pair Coding .

    在AnimationDidStop代理中是否有更好的CAAnimation/CatTransition标识技术?

    谢谢, ——Batgar

    10 回复  |  直到 8 年前
        1
  •  87
  •   vocaro    16 年前

    巴加的技术太复杂了。为什么不利用addAnimation中的forkey参数呢?它就是为了这个目的。只需取出对setValue的调用,并将键字符串移动到addAnimation调用。例如:

    [[hearingAidHalo layer] addAnimation:animation forKey:@"Throb"];
    

    然后,在AnimationDidstop回调中,可以执行以下操作:

    if (theAnimation == [[hearingAidHalo layer] animationForKey:@"Throb"]) ...
    
        2
  •  46
  •   Duncan C    12 年前

    我只是想出了一个更好的方法来完成CAanimations的代码:

    我为一个块创建了typedef:

    typedef void (^animationCompletionBlock)(void);
    

    以及用于向动画添加块的键:

    #define kAnimationCompletionBlock @"animationCompletionBlock"
    

    然后,如果我想在CAAnimation完成后运行动画完成代码,我将自己设置为动画的代理,并使用setValue:forkey:

    animationCompletionBlock theBlock = ^void(void)
    {
      //Code to execute after the animation completes goes here    
    };
    [theAnimation setValue: theBlock forKey: kAnimationCompletionBlock];
    

    然后,我实现了一个animationdIdStop:finished:method,该方法检查指定键处的块,如果找到,则执行它:

    - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
    {
      animationCompletionBlock theBlock = [theAnimation valueForKey: kAnimationCompletionBlock];
      if (theBlock)
        theBlock();
    }
    

    这种方法的好处在于,可以在创建动画对象的同一位置编写清理代码。更好的是,由于代码是一个块,它可以访问在其定义的封闭范围内的局部变量。你不必费心设置用户信息字典或其他类似的胡说八道,也不必编写不断增长的动画:完成:当你添加不同类型的动画时,方法变得越来越复杂。

    事实上,CAAnimation应该有一个内置的完成块属性,并且系统支持在指定完成块属性时自动调用它。但是,上面的代码只提供了几行额外代码,从而提供了相同的功能。

        3
  •  33
  •   jimt    16 年前

    第二种方法只在将动画显式设置为 在运行前在完成时移除:

    CAAnimation *anim = ...
    anim.removedOnCompletion = NO;
    

    如果不这样做,动画将在完成之前被删除,回调将在字典中找不到它。

        4
  •  29
  •   Tibidabo    12 年前

    所有其他答案都太复杂了!为什么不添加自己的关键点来识别动画?

    这个解决方案非常简单,你所需要的就是 为动画添加自己的关键点 (本例中的animationid)

    插入此行以标识 动画1 :

    [myAnimation1 setValue:@"animation1" forKey:@"animationID"];
    

    这是为了确定 动画2 :

    [myAnimation2 setValue:@"animation2" forKey:@"animationID"];
    

    像这样测试:

    - (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag
    {
        if([[animation valueForKey:@"animationID"] isEqual:@"animation1"]) {
        //animation is animation1
    
        } else if([[animation valueForKey:@"animationID"] isEqual:@"animation2"]) {
        //animation is animation2
    
        } else {
        //something else
        }
    }
    

    它不需要任何实例变量 :

        5
  •  12
  •   Stuart    14 年前

    为了明确上面所暗示的内容(以及在浪费了几个小时之后我来到这里的内容):不要期望看到您分配给您的原始动画对象通过

     - (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)flag 
    

    当动画结束时,因为 [CALayer addAnimation:forKey:] 制作动画的副本。

    您可以依赖的是,为动画对象提供的键控值在与传递的复制动画对象中仍然具有等效值(但不一定是指针等效值)。 animationDidStop:finished: 消息。如前所述,使用kvc可以获得足够的范围来存储和检索状态。

        6
  •  1
  •   apinho    9 年前

    我可以看到大部分objc答案,我将根据上面的最佳答案为swift 2.3制作一个答案。

    首先,最好将所有这些键存储在私有结构上,这样它是类型安全的,将来更改它不会给您带来恼人的错误,因为您忘记了在代码中到处更改它:

    private struct AnimationKeys {
        static let animationType = "animationType"
        static let volumeControl = "volumeControl"
        static let throbUp = "throbUp"
    }
    

    如您所见,我已经更改了变量/动画的名称,所以更清楚了。现在,在创建动画时设置这些关键点。

    volumeControlAnimation.setValue(AnimationKeys.volumeControl, forKey: AnimationKeys.animationType)
    

    (…)

    throbUpAnimation.setValue(AnimationKeys.throbUp, forKey: AnimationKeys.animationType)
    

    然后在动画停止时处理代理

    override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
        if let value = anim.valueForKey(AnimationKeys.animationType) as? String {
            if value == AnimationKeys.volumeControl {
                //Do volumeControl handling
            } else if value == AnimationKeys.throbUp {
                //Do throbUp handling
            }
        }
    }
    
        7
  •  0
  •   Teemu Kurppa    16 年前

    imho使用苹果的关键价值是一种优雅的方式:它专门用于向对象添加特定于应用程序的数据。

    另一种更不优雅的可能性是存储对动画对象的引用,并进行指针比较以识别它们。

        8
  •  0
  •   Sirisilp Kongsilp    13 年前

    让我检查2个cabasication对象是否是相同的动画, 我使用keypath函数来实现这一点。

    如果([AnimationA KeyPath]==[AnimationB KeyPath])

    • 无需为cabasication设置关键路径,因为它将不再具有动画效果。
        9
  •  0
  •   Andrei Marincas iSeeker    11 年前

    我喜欢使用 setValue:forKey :要保持我正在设置动画的视图的引用,它比根据ID唯一标识动画更安全,因为相同类型的动画可以添加到不同的层。

    这两个相当:

    [UIView animateWithDuration: 0.35
                     animations: ^{
                         myLabel.alpha = 0;
                     } completion: ^(BOOL finished) {
                         [myLabel removeFromSuperview];
                     }];
    

    有了这个:

    CABasicAnimation *fadeOut = [CABasicAnimation animationWithKeyPath:@"opacity"];
    fadeOut.fromValue = @([myLabel.layer opacity]);
    fadeOut.toValue = @(0.0);
    fadeOut.duration = 0.35;
    fadeOut.fillMode = kCAFillModeForwards;
    [fadeOut setValue:myLabel forKey:@"item"]; // Keep a reference to myLabel
    fadeOut.delegate = self;
    [myLabel.layer addAnimation:fadeOut forKey:@"fadeOut"];
    myLabel.layer.opacity = 0;
    

    在委托方法中:

    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
    {
        id item = [anim valueForKey:@"item"];
    
        if ([item isKindOfClass:[UIView class]])
        {
            // Here you can identify the view by tag, class type 
            // or simply compare it with a member object
    
            [(UIView *)item removeFromSuperview];
        }
    }
    
        10
  •  0
  •   Eng Yew    8 年前

    Xcode 9 Swift 4.0代码

    可以使用键值将添加到AnimationDidstop委托方法中返回的动画的动画关联起来。

    声明字典以包含所有活动动画和相关完成:

     var animationId: Int = 1
     var animating: [Int : () -> Void] = [:]
    

    添加动画时,请为其设置关键点:

    moveAndResizeAnimation.setValue(animationId, forKey: "CompletionId")
    animating[animationId] = {
        print("completion of moveAndResize animation")
    }
    animationId += 1    
    

    在AnimationDidStop中,魔法发生了:

        let animObject = anim as NSObject
        if let keyValue = animObject.value(forKey: "CompletionId") as? Int {
            if let completion = animating.removeValue(forKey: keyValue) {
                completion()
            }
        }
    
    推荐文章