Core Animation을 이용한 애니메이션
좀더 다양한 애니메이션 흐름을 만들기 위해서는 Core Animation(이하 CA)이 필요합니다.
제가 아는 한도 내에서, CA이 필요한 경우는 다음과 같습니다.
1. 애니메이션의 Curve를 조정하고 싶은 경우 (예 : 바운스)
2. CALayer의 애니메이션 가능한 모든 프로퍼티를 사용하고 싶은 경우
3. 그 외에 좀더 복잡한 애니메이션을 조직화하고 싶은 경우 (안해봄)
CA의 기본 객체는 CALayer와 CAAnimation으로, CALayer를 사용하는 법에 대해서는 앞서 알아본 바 있습니다.
CAAnimation은 애니메이션을 표시하는 기본적인 추상 클래스입니다. 다음과 같은 상속 구조를 가집니다.
CAAnimation : 기본 애니메이션 클래스
CAPropertyAnimation : CALayer의 속성을 조정하는 메소드들을 포함합니다.
CABasicAnimation : 세 개의 interpolation 값(from, by, to)을 통해 애니메이션 흐름을 조절하는 기본 애니메이션 객체입니다.
CAKeyframeAnimation : keyframe 애니메이션을 위한 클래스입니다. 애니메이션 흐름을 보다 세밀하게 조정하기 위해 사용합니다.
CABasicAnimation 시작하기
layer의 속성을 조정하는 간단한 애니메이션 코드를 만들어 보겠습니다.
- (void)layerAnimation1
{
CABasicAnimation* ani = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];
ani.duration = 3.0f;
ani.beginTime = CACurrentMediaTime() + 2.0f;
ani.fromValue = [NSNumber numberWithFloat:0.0];
ani.toValue = [NSNumber numberWithFloat:50.0];
[self.itemView.layer addAnimation:ani forKey:@"ani"];
}
첫 줄은 레이어의 cornerRadius를 변경하는 애니메이션을 만드는 선언입니다.
duration은 UIView animation의 duration과 같습니다. 애니메이션이 실행되는 시간입니다.
beginTime은 delay에 해당합니다. 다만 그냥 숫자를 넣으면 안되고, 현재 시간을 의미하는 CACurrentMediaTime() 값에 더해야 합니다.
fromValue와 toValue는 애니메이션의 시작값과 끝값을 의미하는데, 둘다 숫자가 아닌 객체를 인자로 사용합니다.
이렇게 하는 이유는, keyPath가 scalar가 아닌 값들(CGPoint, CGSize, CGRect)도 사용할 수 있도록 설계되었기 때문입니다.
마지막 줄에서는 만들어진 애니메이션 객체를 실행할 레이어에 더해줍니다.
이렇게 애니메이션을 실행하면 UIView Animation에서는 변경이 불가능했던 cornerRadius를 변경하는 애니메이션을 볼 수 있습니다.
여기서 한가지, UIView Animation과는 다른 점이 있는데, CA에서는 애니메이션이 끝나면 레이어의 속성을 원래 값으로 되돌린다는 것입니다.
만일 UIView Animation과 같이, 애니메이션이 끝난 상태로 레이어의 값을 조정하고 싶다면, 애니메이션이 끝난 후 레이어의 값을 직접 최종값으로 세팅해야 합니다.
애니메이션의 시작과 끝을 통보받기 위해서는 다음과 같이 CAAnimation의 delegate를 구현해야 합니다.
- (void)layerAnimationWithDelegate
{
CABasicAnimation* ani = [CABasicAnimation animationWithKeyPath:@"cornerRadius"];
ani.duration = 3.0f;
ani.beginTime = CACurrentMediaTime() + 2.0f;
ani.fromValue = [NSNumber numberWithFloat:0.0];
ani.toValue = [NSNumber numberWithFloat:50.0];
ani.delegate = self;
[self.itemView.layer addAnimation:ani forKey:@"ani"];
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
self.itemView.layer.cornerRadius = 50.0f;
}
이렇게 하면 애니메이션이 종료된 후 콜백을 통해 레이어의 값을 바꿀 수 있습니다.
하지만 이 방법은 하나의 객체에서 여러개의 애니메이션을 사용할 경우 코드가 복잡해지는 문제점을 안고 있습니다.
UIView의 애니메이션처럼 block을 이용해서 콜백을 받는 것이 좋겠지만 지원되지 않습니다.
따라서 이럴 때 흔히 사용하는 해법인 Associative Reference를 사용하곤 합니다. 이 부분은 따로 알려드리도록 하겠습니다.
keyPath는 scalar값만 사용할 수 있는 것이 아니라고 위에 이야기했습니다. 이럴 경우 fromValue나 toValue에 NSNumber가 아닌 NSValue를 사용합니다.
레이어의 transform 속성을 조정하는 예제를 보겠습니다.
- (void)layerAnimationWithNSValue
{
CABasicAnimation* ani = [CABasicAnimation animationWithKeyPath:@"transform"];
ani.duration = 3.0f;
CATransform3D startTransform = CATransform3DIdentity;
CATransform3D endTransform = CATransform3DMakeTranslation(30, 30, 0);
ani.fromValue = [NSValue valueWithCATransform3D:startTransform];
ani.toValue = [NSValue valueWithCATransform3D:endTransform];
[self.itemView.layer addAnimation:ani forKey:@"ani"];
}
혹은 transform중 일부의 값만 조정하고 싶을 수도 있습니다. 이런 문법도 사용이 가능합니다.
- (void)layerAnimationWithTransformEx
{
CABasicAnimation* ani = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];
ani.duration = 3.0f;
ani.fromValue = [NSNumber numberWithFloat:0.0];
ani.toValue = [NSNumber numberWithFloat:50.0];
[self.itemView.layer addAnimation:ani forKey:@"ani"];
}
이런 방식으로 조정 가능한 속성들에 대해서는 다음 링크를 참고하시면 됩니다.
CAMediaTimingFunction & Bezier Curve
위에서 UIView에서 애니메이션이 움직이는 방법을 조정하는 Easing Function에 대해 알아본 바 있습니다.
UIView Animation은 매우 제한적인 Easing만을 제공하는데, CA에서는 보다 다양한 애니메이션을 사용할 수 있습니다.
이때 사용하는 것이 CAMediaTimingFunction입니다. 우선은 UIView Animation과 같은 일을 하는 간단한 예제를 보겠습니다.
- (void)animationWithBasicMediaTimingFunction
{
CABasicAnimation* ani = [CABasicAnimation animationWithKeyPath:@"position.x"];
ani.duration = 3.0f;
ani.fromValue = [NSNumber numberWithFloat:160.0];
ani.toValue = [NSNumber numberWithFloat:280.0];
ani.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[self.itemView.layer addAnimation:ani forKey:@"ani"];
}
이처럼 CAAnimation 객체에 timingFunction을 적용함으로써, UIView Animation에서 사용했던 easing function을 CA에서도 사용할 수 있습니다.
다만 CAMediaTimingFunction에서는 기본 제공되는 함수 외에, control point를 사용해서 애니메이션의 동작(curve)를 조정하는 방법을 제공합니다.
여기에서 베지어 곡선이 등장합니다. 베지어 곡선은 컴퓨터 그래픽에서 곡선을 그리기 위해 사용하는 대중적인 방법입니다.
포토샵과 같은 그래픽 툴에서 곡선을 그릴때 나오는 모양이 바로 베지어 곡선입니다.
CA에서는 3차 베지어 곡선(혹은 cubic bezier라고도 합니다)을 사용해서 애니메이션의 커브를 조정할 수 있습니다.
3차 베지어 곡선은 시작점과 끝점, 그리고 중간점 두개로 정의되는데, CA에서 시작점은 항상 0, 끝점은 항상 1이며, 나머지 두개의 중간점을 control point라고 합니다.
이부분은 원래 머리가 좀 아프지만, 누군가가 여기 사용하라고 아주 아름다운 사이트를 만들어 주었습니다.
위 사이트에 방문해서, 빨간 점과 파란 점을 요리조리 움직여서 곡선의 모양을 정하고, 데모를 볼 수 있습니다.
빨간 점과 파란 점을 움직일 때 변화하는 네 개의 숫자가 control point입니다. 이 숫자를 이용해서 애니메이션을 만들어 보겠습니다.
- (void)animationWithControlPoints
{
CABasicAnimation* ani = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
ani.duration = 3.0f;
ani.fromValue = [NSNumber numberWithFloat:0.5];
ani.toValue = [NSNumber numberWithFloat:1.0];
ani.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.6 :0.22 :0.9 :1.76];
[self.itemView.layer addAnimation:ani forKey:@"ani"];
}
약간 어설프긴 하지만, 일종의 바운스 애니메이션을 만들 수 있습니다.
CAAnimationGroup
여러가지 애니메이션을 동시에 실행하기 위해 CAAnimationGroup을 사용할 수 있습니다.
CAAnimationGroup은, 하나의 레이어에서 여러개의 속성을 동시에 애니메이션할 때 사용합니다.
이때 duration은 CAAnimationGroup의 것을 따라갑니다. 하나의 그룹 안에 duration이 더 긴 애니메이션이 있다면, 해당 애니메이션은 group의 duration이 끝나는 시점까지만 움직입니다.
- (void)animationWithAnimaionGroup
{
CABasicAnimation* ani1 = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
ani1.duration = 3.0f;
ani1.fromValue = [NSNumber numberWithFloat:0.5];
ani1.toValue = [NSNumber numberWithFloat:1.0];
CABasicAnimation* ani2 = [CABasicAnimation animationWithKeyPath:@"position.x"];
ani2.duration = 10.0f;
ani2.fromValue = [NSNumber numberWithFloat:160.0];
ani2.toValue = [NSNumber numberWithFloat:300.0];
CAAnimationGroup* aniGroup = [CAAnimationGroup animation];
aniGroup.animations = @[ani1, ani2];
aniGroup.duration = 3.0f;
[self.itemView.layer addAnimation:aniGroup forKey:@"ani"];
}
Evernote Data:
{"source": "missing value", "created": "2014년 5월 22일 목요일 오후 4:21:51", "modified": "2014년 5월 26일 월요일 오후 12:51:04", "tags": "", "altitude": "33.091815948486", "latitude": "37.506928499597", "longitude": "127.067404838655" }