코어애니메이션 시작하기

코코아 터치에서 코어 애니메이션을 사용할 일은 게임 류의 앱을 만들기 전에는 그리 많이 필요하지 않을 수 있다. 하지만 여러 가지 효과가 한 번에 결합된 애니메이션이나 키 프레임 애니메이션[1. 키 프레임 애니메이션은 보통 여러 이미지를 순차적으로 표시하는 애니메이션으로 예제들이 많이 나오는데, 사실은 애니메이션 중간 중간에 타겟을 설정하고 순서대로 흘러가는 애니메이션이다. 패스를 따라 움직이는 애니메이션이 이런 키 프레임 애니메이션으로 구현된다.]을 적용하는 것을 가능하게 한다. 이번 포스트에서는 iOS에서 코어 애니메이션을 구현하는 법에 대해 알아보고자 한다.

코어 애니메이션은 레이어 기반으로 애니메이션이 구현된다. 이 점은 코코아 애니메이션과 크게 다르지 않다. 또한 상당히 세분화된 애니메이션이 가능하다고 했는데 이것을 개념적으로 먼저 짚고 넘어가고자 한다.

먼저 코어 애니메이션은 그 종류에 따라 CABasicAnimationCAKeyFrameAnimation, CATransition으로 구분된다. CABasicAnimation은 각각의 레이어요소에 대한 기본적인 애니메이션이다. CAKeyFrameAnimation은 패스를 따라 움직이는 등, 주어진 키 프레임 사이를 이동하는 애니메이션이 될 수 있다. CATransition은 레이어가 나타나거나 사라질 때 적용되는 레이어 컨텐츠 전체에 대한 애니메이션이다. 이들은 약간의 상속 구조가 다르지만, 대체로 기본적인 사용방법은 동일하다고 보면 되고, 이들의 사용 방법을 알아보자.

코어 애니메이션 레이어 (CALayer)는 각각의 UIView에 하나씩 기본적으로 적용되어 있다. 뷰가 계층구조를 가지는 것과 같이, 레이어도 계층 구조를 가질 수 있다. 즉 뷰 하나에 여러 개의 CALayer가 들어갈 수 있다는 것이다. 각각의 레이어는 애니메이션 스택을 가지고 있다. 여기에 CAAnimation 객체를 만들어서 추가하면 레이어는 해당 애니메이션을 실행하게 된다. 즉, 다음과 같은 방식으로 작업을 진행할 수 있다.

  1. CALayer 객체를 생성한다.
  2. CALayer에 그려질 컨텐츠를 입힌다. (이미지나 패스를 그린다)
  3. CAAnimation 객체를 생성한다.
  4. 애니메이션 객체에 재생시간, 가속곡선, 초기값, 최종값을 정의해준다.
  5. 완성된 애니메이션 객체를 레이어의 animations에 추가한다. 이 때 해당 애니메이션이 레이어의 어떤 속성에 적용되는 것인지 키패스를 사용하여 명시한다.
  6. 애니메이션 스택을 장착한 레이어를 현재 뷰의 레이어에 서브 레이어로 추가한다.
  7. 이제 코어 애니메이션 프레임워크에 의해 애니메이션이 재생된다.

예제 – 궤도를 따라 도는 원 만들기

궤도를 따라 원이 화면에서 회전하는 애니메이션을 구현해보도록 하겠다. 이를 위해서는 새 프로젝트를 생성하고 QuartzCore 프레임워크를 연결해야 한다.

루트 뷰 컨트롤러의 viewDidload에서 모든 게 구현된다.

-(void)viewDidLoad
{
// 궤도가 되는 원을 그리는 레이어를 생성한다. 원을 그리지 않고,
// 레이어의 모서리를 둥글게하여 레이어 외곽선으로 궤도를 표현한다.
CALayer *orbit1 = [CALayer layer];
orbit1.bounds = CGRectMake(0,0,200,200);
orbit1.position = self.view.center;
orbit1.cornerRadius = 100;
orbit1.borderColor = [UIColor redColor].CGColor;
orbit1.borderWidth = 1.5;

// 궤도를 따라 도는 위성을 그리고 이를 궤도 레이어에 추가한다.
CALayer *planet1 = [CALayer layer];
planet1.bounds = CGRectMake(0,0,20,20);
planet1.position = CGPointMake(100,0);
planet1.cornerRadius = 10;
planet1.backgroundColor = [UIColor redColor].CGColor;
[orbit1 addSubLayer:planet1];

// 궤도는 원형으로 보일 것이므로 궤도 레이어 자체를 회전할 것이다.
// 궤도의 회전에 쓰일 애니메이션을 생성한다.
CABasicAnimation *ani1 = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
ani1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
ani1.fromValue = [NSNumber numberWithFloat:0];
ani1.toValue = [NSNumber numberWithFloat:(360*M_PI)/180)];
ani1.repeatCount = HUGE_VALF;
ani1.duration = 5.0;
[orbit1 addAnimation:ani1 forKey:@"transform"];

// 궤도 레이어를 뷰에 표시한다.
[self.view.layer addSublayer:orbit1];
}

궤도를 따라 도는 빨간색 위성 주위를 도는 다른 위성을 하나 더 만들어보도록 하겠다. 색상과 크기만 다르고 완전히 동일한 코드이다. 아래 코드를 위의 orbit1 레이어를 위 코드의 맨 아래쪽에 추가해주자.

CALayer *orbit2 = [CALayer layer];
orbit2.bounds = CGRectMake(0,0,120,120);
orbit2.position = self.view.center;
orbit2.cornerRadius = 60;
orbit2.borderColor = [UIColor blueColor].CGColor;
orbit2.borderWidth = 1.5;

CALayer *planet2 = [CALayer layer];
planet2.bounds = CGRectMake(0,0,16,16);
planet2.position = CGPointMake(60,0);
planet2.cornerRadius = 30;
planet2.backgroundColor = [UIColor blueColor].CGColor;
[orbit2 addSubLayer:planet2];

CABasicAnimation *ani2 = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
ani2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
ani2.fromValue = [NSNumber numberWithFloat:0];
ani2.toValue = [NSNumber numberWithFloat:(360*M_PI)/180)];
ani2.repeatCount = HUGE_VALF;
ani2.duration = 5.0;
[orbit2 addAnimation:ani1 forKey:@"transform"];

// 궤도 레이어를 뷰에 표시할 때, 큰 궤도에 덧붙인다.
[orbit1 addSubLayer:orbit2];

참고로 CALayer와 CGLayer는 글자 하나 차이지만, 완전히 다르다. CGLayer는 고성능/고품질을 위한 그래픽처리를 위해 만들어졌고, 실제 사용 용도는 off-screen 렌더링에 사용된다. 이에 비해 CALayer는 실제 뷰가 어떻게 움직일 것인지를 레이어로 추상화하여 처리하기 위한 것으로 둘은 개념도 다르고 서로 호환되지 않는다.