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


102

일련의 겹치는 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


4
Batgar, 내가 "iphone animationDidStop 식별"을 검색했을 때 첫 번째 히트작은 애니메이션을 식별하기 위해 키-값 사용을 제안하는 귀하의 게시물이었습니다. 내가 필요한 건 고마워요 Rudi
rudifa

1
주의하십시오 CAAnimation's는 delegate당신이 그것을 설정해야 할 수도 있으므로, 강한 nil사이클을 유지 피하기 위해!
Iulian Onofrei 2016-08-10

답변:


92

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

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

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

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

위의 사용하면 RETAIN COUNT가 증가한다는 것을 언급하고 싶습니다! 경고 받다. 즉, animationForKey : CAAnimation 객체의 보유 수를 증가시킵니다.
mmilo 2010

1
@mmilo 그다지 놀랍지 않습니까? 레이어에 애니메이션을 추가하면 레이어가 애니메이션을 소유하므로 애니메이션의 보유 횟수는 물론 증가합니다.
GorillaPatch 2010

16
작동하지 않음-중지 선택기가 호출 될 때까지 애니메이션이 더 이상 존재하지 않습니다. 널 참조를 얻습니다.
Adam

4
이는 forKey : 매개 변수의 오용이며 필요하지 않습니다. Batgar가하는 일은 정확히 옳았습니다. 키-값 코딩을 사용하면 애니메이션에 임의의 데이터를 첨부 할 수 있으므로 쉽게 식별 할 수 있습니다.
매트

7
Adam, 아래의 jimt의 답변을 참조하십시오 . 호출 anim.removedOnCompletion = NO;될 때 여전히 존재하도록 설정해야합니다 -animationDidStop:finished:.
Yang Meyer

46

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


7
: 누군가는이에 대한 CAAnimation 함께 범주를 넣어 github.com/xissburg/CAAnimationBlocks
제이 페 이어

이것은 옳지 않은 것 같습니다. 꽤 자주 EXEC_Err theBlock();이 호출 된 직후 에 발생하며 블록 범위가 파괴 되었기 때문이라고 생각합니다.
mahboudz 2012

나는 블록을 한동안 사용 해왔고, 애플의 끔찍한 "공식적인"접근 방식보다 훨씬 더 잘 작동한다.
Adam

3
속성에 대한 값으로 설정하기 전에 해당 블록을 [블록 복사]해야한다고 확신합니다.
Fiona Hopkins

1
아니요, 블록을 복사 할 필요가 없습니다.
Duncan C

33

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

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

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


10
이것은 답변이 아니라 의견이어야합니다.
까지

2
나중에 removeAnimationForKey를 사용하여 명시 적으로 제거해야하는지 궁금합니다.
bompf

그것은 당신이 무엇을하고 싶은지에 달려 있습니다. 필요한 경우 제거하거나 다른 작업을 동시에 수행하고 싶기 때문에 그대로 둘 수 있습니다.
applejack42 dec. 05

31

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

이 솔루션은 애니메이션에 고유 한 키를 추가하기 만하면 매우 쉽습니다 (이 예 에서는 애니메이션 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
    }
}

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


나는 같은 animationDidStop 일부 int 값을 (INT (0)) 무엇입니까[animation valueForKey:@"animationID"]
abhimuralidharan

14

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

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

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

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


1
+1 이것이 최고의 솔루션입니다! 를 사용하여 애니메이션의 '이름'을 [animation setValue:@"myanim" forKey:@"name"]설정할 수 있으며을 사용하여 애니메이션되는 레이어를 설정할 수도 있습니다 [animation setValue:layer forKey:@"layer"]. 그런 다음 대리자 메서드 내에서 이러한 값을 검색 할 수 있습니다.
trojanfoe

valueForKey:nil나를 위해 돌아온 이유는 무엇입니까?
Iulian Onofrei 2016 년

@IulianOnofrei는 동일한 속성에 대해 애니메이션이 다른 애니메이션으로 대체되지 않았는지 확인합니다. 예기치 않은 부작용으로 발생할 수 있습니다.
t0rst

@ t0rst, 죄송합니다. 여러 애니메이션이 있고 복사 붙여 넣기를 사용하여 동일한 애니메이션 변수에 다른 값을 설정했습니다.
Iulian Onofrei

2

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

0

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

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


이것은 작동하지 않습니다. Apple이 포인터를 변경하기 때문에 포인터 등가를 수행 할 수 없습니다.
Adam

0

2 개의 CABasicAnimation 객체가 동일한 애니메이션인지 확인하기 위해 keyPath 함수를 사용하여 정확히 수행합니다.

if ([animationA keyPath] == [animationB keyPath])

  • 더 이상 애니메이션이 적용되지 않으므로 CABasicAnimation에 대한 KeyPath를 설정할 필요가 없습니다.

문제는 콜백을 위임에 관한 것으로, 키 패스는 CAAnimation의 방법이 아닙니다
최대 MacLeod는

0

I like to use 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];
    }
}

0

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()
        }
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.