[iphone] animationDidStop 델리게이트 내에서 CAAnimation을 식별하는 방법은 무엇입니까?

일련의 겹치는 CATransition / CAAnimation 시퀀스가있는 문제가있었습니다.이 시퀀스는 모두 애니메이션이 중지되었을 때 사용자 지정 작업을 수행해야했지만 animationDidStop에 대한 하나의 델리게이트 핸들러 만 원했습니다.

그러나 문제가 있었는데 animationDidStop 델리게이트에서 각 CATransition / CAAnimation을 고유하게 식별하는 방법이없는 것 같습니다.

CAAnimation의 일부로 노출 된 키 / 값 시스템을 통해이 문제를 해결했습니다.

애니메이션을 시작할 때 CATransition / 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.
 }

이것의 다른 측면은 델리게이트 클래스에 저장하는 대신 키 값 페어링 시스템에서 상태를 유지할 수 있다는 것입니다. 코드가 적을수록 좋습니다.

Key Value Pair Coding에 대한 Apple Reference 를 확인하십시오 .

animationDidStop 델리게이트에서 CAAnimation / CATransition 식별을위한 더 나은 기술이 있습니까?

감사합니다. -Batgar



답변

Batgar의 기술은 너무 복잡합니다. addAnimation에서 forKey 매개 변수를 활용하지 않는 이유는 무엇입니까? 바로이 목적을위한 것입니다. setValue에 대한 호출을 꺼내고 키 문자열을 addAnimation 호출로 이동하면됩니다. 예를 들면 :

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

그런 다음 animationDidStop 콜백에서 다음과 같이 할 수 있습니다.

if (theAnimation == [[hearingAidHalo layer] animationForKey:@"Throb"]) ...


답변

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 : 메서드를 구현합니다.

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

이 접근 방식의 장점은 애니메이션 개체를 만드는 동일한 위치에 정리 코드를 작성할 수 있다는 것입니다. 더 좋은 점은 코드가 블록이기 때문에 정의 된 둘러싸는 범위의 지역 변수에 액세스 할 수 있다는 것입니다. userInfo 사전이나 다른 말도 안되는 설정을 엉망으로 만들 필요가 없으며, 다양한 종류의 애니메이션을 추가함에 따라 점점 더 복잡 해지는 지속적으로 성장하는 animationDidStop : finished : 메소드를 작성할 필요가 없습니다.

사실 CAAnimation에는 완성 블록 속성이 내장되어 있어야하며, 지정된 경우이를 자동으로 호출하기위한 시스템 지원이 있어야합니다. 그러나 위의 코드는 몇 줄의 추가 코드만으로 동일한 기능을 제공합니다.


답변

두 번째 방법은 애니메이션을 실행하기 전에 완료시 제거 되지 않도록 명시 적으로 설정 한 경우에만 작동 합니다.

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

그렇게하지 않으면 애니메이션이 완료되기 전에 제거되고 콜백이 사전에서 찾을 수 없습니다.


답변

다른 모든 답변은 너무 복잡합니다! 애니메이션을 식별하기 위해 자신의 키를 추가하지 않는 이유는 무엇입니까?

이 솔루션은 애니메이션에 고유 한 키를 추가하기 만하면 매우 쉽습니다 (이 예 에서는 애니메이션 ID ).

animation1 을 식별하려면 다음 줄을 삽입하십시오 .

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

그리고 이것은 animation2 를 식별하기 위해 :

[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
    }
}

인스턴스 변수가 필요하지 않습니다 .


답변

위에서 암시 한 내용 (그리고 몇 시간을 낭비한 후 여기로 온 것)을 명시하려면 할당 한 원래 애니메이션 개체가 다시 전달 될 것이라고 기대하지 마세요.

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

애니메이션이 끝나면 애니메이션 [CALayer addAnimation:forKey:]의 복사본을 만들기 때문 입니다.

신뢰할 수있는 것은 애니메이션 개체에 제공 한 키 값이 animationDidStop:finished:메시지 와 함께 전달 된 복제본 애니메이션 개체에 동일한 값 (포인터 등가 일 필요는 없음)과 함께 여전히 존재한다는 것입니다 . 위에서 언급했듯이 KVC를 사용하면 상태를 저장하고 검색 할 수있는 충분한 범위를 얻을 수 있습니다.


답변

위의 베스트 답변을 기반으로 신속한 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
        }
    }
}


답변

Apple의 키-값을 사용하는 IMHO는이를 수행하는 우아한 방법입니다. 이는 특히 애플리케이션 특정 데이터를 객체에 추가 할 수 있도록하기위한 것입니다.

훨씬 덜 우아한 가능성은 애니메이션 객체에 대한 참조를 저장하고이를 식별하기 위해 포인터 비교를 수행하는 것입니다.