[Cocoa Touch] 뷰 트랜지션 애니메이션 구현하기

트랜지션(transition)은 뷰가 갑자기 나타내거나 없어지거는 것과 관련하여 보다 나은 사용자 경험을 제공할 수 있도록 해준다. 트랜지션 역시 뷰 애니메이션이므로 UIView의 애니메이션 메소드를 사용하거나 코어 애니메이션을 사용하여 구현하는 두 가지 방법 중 하나를 선택할 수 있다.

UIView 애니메이션으로 뷰 트랜지션 구현

UIView 애니메이션에서 지원하는 트랜지션은 크게 두 가지로 나뉜다.

  • 기존의 뷰의 서브 뷰들을 바꾸기 – 비교적 작은 뷰의 변경에 속한다.
  • 뷰를 새로운 뷰로 대체하기 – 화면상에서 변경이 일어나는 영역이 많을 때

뷰 트랜지션은 뷰 컨트롤러에 의한 트랜지션과는 다르다. 뷰 컨트롤러 트랜지션은 window에 표시되는 전체 뷰가 바뀌는 것이지만, 여기서 말하는 뷰 트랜지션은 현재 뷰의 계층 구조 내에서의 변화가 일어나는 것을 의미한다.(즉 네비게이션 컨트롤러 상의 뷰 스택에는 변경이 없다.)

뷰의 서브 뷰 변경하기

iOS4에서부터 trasitionWithView:duration:options:animations:completion: 메소드가 제공되는데, 이를 사용하면 특정 뷰의 서브 뷰에 대한 트랜지션을 쉽게 구현할 수 있다. 이 메소드에 전달되는 블럭에서는 뷰의 전환과 관련된 변경 사항만을 구현해야 한다. 코코아 터치는 성능의 향상을 위해서 변경 전과 후의 뷰의 스냅샷을 찍어 이 두 이미지를 부드럽게 전환하도록 한다. 만약 뷰의 교체외에 다른 변경 (뷰의 크기가 변경된다거나 배경색이 변경되는등)을 동시에 수행해야 하면, UIViewAnimationOptionAllowAnimatedContent 옵션을 줘야 한다. 이 옵션이 주어지면 코코아터치는 더 이상 두 스냅샷 사이를 전환하는 기능을 사용하지 않고 모든 서브 뷰에 대해 직접 애니메이션을 생성하게 된다.

위 메소드의 원형을 먼저 살펴보자.

+ (void)transitionWithView:(UIView*)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void(^)(void))animations completion:(void(^)(BOOL finished))completion;

다음은 각각의 파라미터에 대한 설명

  • view : 트랜지션이 발생하는 컨테이너 뷰. 이 뷰의 서브 뷰들이 전환된다.
  • duration : 트랜지션 지속시간
  • options : 애니메이션 옵션에 대한 비트마스크 값들.
  • animations : 애니메이션 블럭. 어떤 서브뷰가 없어지고 어떤 서브뷰가 추가되는지를 기술해야 한다.
  • completion : 트랜지션 종료 후 실행할 블럭.

이를 수행하는 예제는 다음과 같이 쓸 수 있다. 코드 블럭 내에서 view의 서브 뷰를 제거하고 다른 뷰를 추가한다.

[UIView transitionWithView:containerView
    duration:0.2f
    options:UIViewAnimatinoOptionTransitionFlipFromLeft
    animations:^{
        [fromView removeFromSuperview];
        [containerView addSubview:toView];
    }
    completion:NULL];

특정한 뷰를 다른 뷰로 교체하기

그외에 다음의 메소드들도 발견할 수 있는데…

+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void(^)(BOOL finished)completion;

이 메소드는 두 뷰간의 전환을 구현하는 가장 간단한 방법이다. 만약 두 뷰 모두 뷰 계층 구조에 이미 속해 있다면, UIViewAnimationOptionSHowHideTransitionViews를 추가해줄 수 있다. 이 메소드는 기본적으로 현재 컨테이너 뷰의 계층 구조를 변경한다. (fromViewtoView로 교체한다.)

UIView 애니메이션은 기본적으로 사용자 입력을 블럭킹하는데, 이를 피하기 위해서는 UIViewAnimationOptionAllowUserInteraction을 추가한다. 다음 코드는 뷰 컨트롤러의 메인뷰(루트뷰)를 위해 두 개의 뷰를 준비해 놓고 이들을 교체하는 트랜지션 효과를 구현한 것이다. (어느 뷰를 보여주고 있느냐에 대해서 트랜지션 방향이 바뀐다.)

    -(IBAction)toggleMainView:(id)sender
{
    [UIView transitionFromView:(displayingPrimary ? primaryView : secondaryView)
    toView:(displayingPrimary ? secondaryView : primaryView)
    duration:1.0f;
    options: ( displayingPrimary ? 
    UIViewAnimationOptionTransitionFlipFromRight : UIViewAnimationOptionTransitionFlipFromLeft)
    completion:^(BOOL finished) {
        if(finished) {
            displayingPrimary = !displayingPrimary;
        }
    }];
}

실제로 위 코드를 활용하기 위해서 뷰 컨트롤러는 뷰를 로드/언로드하는 처리를 해주어야 한다. 즉 트랜지션이 일어나기 직전에 toView가 될 뷰를 로드해서 생성해주고, 교체해야 할 것이다.

코어 애니메이션으로 트랜지션 구현하기

코어 애니메이션은 (UIView 애니메이션도 내부적으로는 코어 애니메이션으로 전환된다.) 뷰와 연관되어 있는 레이어를 조작하여 애니메이션을 구현하는 고속 그래필 처리 프레임워크이다. 코어 애니메이션은 CAAnimation이라는 추상 클래스를 통해 객체화되는데 이 CAAnimation의 서브 클래스인 CATransition을 사용하면 뷰 간의 트랜지션 효과를 구현할 수 있다.

코어 애니메이션은 뷰의 뒷단에 존재하는 애니메이션 레이어(CALayer)에 의한 애니메이션이다. 뷰와 레이어는 긴밀하게 연결되어 있어서 UIView의 어떤 속성을 변경하면 레이어에도 같은 변경이 가해지고, 그 차이를 통해 애니메이션을 구현할 수 있다.

다음은 두 뷰 간의 트랜지션을 만들어 내는 예제이다.

    CATransition *transition = [CATransition animation];
    transition.startProgress = 0;
    transition.endProgress = 1.0;
    transition.type = kCATransitionPush;
    transition.subType = kCATransitionFromRight;
    transition.duration=1.0;

    [myView.layer addAnimation:transition forKey:@"transition"];
    [myView.layer addAnimation:transition forKey:@"transition"];

    myView1.hidden = YES;
    myView2.hidden = NO;

두 뷰가 트랜지션될 때에는 하나의 동일한 트랜지션 객체를 두 뷰(뷰의 레이어)가 공유하면 된다. 각각의 뷰는 뷰 계층구조에 이미 존재하고 있고 hidden 속성을 끄고 켜는 것으로 뷰에 변경을 가하면, 각각의 뷰에 연결된 CALayer에서 이 변화를 감지, 연결되어 있는 애니메이션 객체(CATransition)에서 트랜지션 애니메이션을 처리하는 형태로 구현된다. 코어 애니메이션에 대한 내용은 별도의 포스트에서 다루도록 하겠다.