일련의 겹치는 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는이를 수행하는 우아한 방법입니다. 이는 특히 애플리케이션 특정 데이터를 객체에 추가 할 수 있도록하기위한 것입니다.
훨씬 덜 우아한 가능성은 애니메이션 객체에 대한 참조를 저장하고이를 식별하기 위해 포인터 비교를 수행하는 것입니다.