본문 바로가기

iOS

[iOS] 화면전환 애니메이션 커스텀하기 (2) UIViewControllerAnimatedTransitioning, (NavigationController)

2020/05/15 - [iOS] - [iOS] 화면전환 애니메이션 커스텀하기 - CGAffineTransform, animateTransition [1]

https://www.raywenderlich.com/2925473-ios-animation-tutorial-custom-view-controller-presentation-transitions

에서 도움을 얻었습니다!

 

다음과 같은 트랜지션 애니메이션을 만들어 낼 것입니다!

 

이번 시간에는, 저번 1편에서 배운 CGAffineTransform()을 이용해서 NavigationController Transition을 만들어 볼 것입니다! 꽤 험난한데, 일단 천천히 가보겠습니다. 사용할 중요한 구조는 두가지입니다.

 


1. 구조 알아보기

Transition의 구조를 먼저 파악해 보겠습니다!

1. 위는 UIViewControllerTransitioningDelegate 내에서 발생하는 일입니다.

UIKit은 animationController(forPresented:presenting:source:)를 호출합니다.

이 친구는 UIViewControllerAnimatedTransitioning 객체를 리턴하는데요, 이 값을 변경해주어야 합니다.

디폴트는 nil입니다. 만약  nil을 리턴받으면 객체는 기본 트랜지션 애니메이션을 가지게 됩니다.

다른 UIViewControllerAnimatedTransitioning 객체를 만들어서, 리턴을 받도록 하겠습니다.

 

2. 애니메이션이 진행중인 동안, animateTransition(..) 이 호출됩니다. 이 animateTransition(..) 에서는 트랜지션이 진행중인 뷰 - 

즉 FromVC와 toVC를 모두 관리할 수 있습니다. fade, scale, rotate와 원하는대로의 커스텀을 이곳에서 할 수 있습니다.

 

두가지를 알아보았는데요, 이를 매개할 딜리게이트를 잠시 알아보면....

바로 얘입니다. 아까 파란색 걔. UIViewControllerAnimatedTransitioning. 

Animate를 관장할 Swift파일을 만들어 줄 것입니다. 그리고 이 파란색 친구를 상속시킵니다.

 


2. 코드 작성하기

천천히 해보겠습니다. 예제의 경우, PopAnimator.swift입니다.

class PopAnimator: NSObject, UIViewControllerAnimatedTransitioning {

}

다음과 같이 선언을 해 주면, 필수적으로 오버라이딩해야 하는 함수 두개가 호출됩니다. 

 

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
    return duration
  }
  
  func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
  
  }

1 : 이름에서 알 수 있듯, transitionDuration은 뷰가 전환될 때의 Duration입니다.  Duration은 미리 상수로 선언해서 0.8입니다.

2 : 아까 노란색 걔입니다. animatedTransition. 여기 안에서 애니메이션을 만들것입니다.

 

1) HomeViewController (FromVC) 

HomeVC와 DetailVC는 Segue로 연결되어 있습니다.

구조는 위와 같습니다. 예상하건데,

Segue는 prepare(for:sender:)를 타고 움직이므로, prepare(for:sender:) 내에 delegate를 선언할 것입니다.

 

HomeVC에서 Transition을 관장하는 delegate를 채택해 보겠습니다!

extension HomeViewController: UIViewControllerTransitioningDelegate {

}

UIViewControllerTransitioningDelegate를 선언해주고, 이 안에서 아까 맨 위에서 설명한 animationController(forPresented:presenting:source:) 함수를 선언할 것입니다.. 너무 어렵죠... 제가 봐도 그런데요...ㅠㅠ

홧팅. 다음은 본격적인 애니메이션 만들기입니다!

 


3. Animator 이용

let transition = PopAnimator()

와 같이 선언합니다. HomeVC입니다.

그다음, Transition이 Custom임을 나타낸다고 했었던 파란색 걔 UIViewControllerAnimatedTransitioning 입니다. 얘를 PopAnimator로 지정해 줄 것입니다. 

func animationController(
  forPresented presented: UIViewController, 
  presenting: UIViewController, source: UIViewController) 
    -> UIViewControllerAnimatedTransitioning? {
  return transition
}

위는 forPresented,  아래는 forDismissed에 해당하는 트랜지션입니다. 두개를 다르게 선언해 줍니다.

func animationController(forDismissed dismissed: UIViewController)
    -> UIViewControllerAnimatedTransitioning? {
  return nil
}

이대로 실행해도 실행은 잘 되는데요, 트랜지션 효과를 넣지 않았으니 별로 예쁜 모양은 나오지 않습니다.

실행을 위해 애니메이션을 추가해 볼게요.

let containerView = transitionContext.containerView
let toView = transitionContext.view(forKey: .to)!
let recipeView = presenting ? toView : transitionContext.view(forKey: .from)!

fromVC와 toVC를 선언했습니다. 아래 두줄은 어렵지 않습니다. 이전 뷰에서 다음 뷰를 key로 받아왔습니다.

presenting은 Bool으로, 트랜지션이 진행중이면 true, 아닌 경우 false입니다.

 

첫 줄의 containerView는 Transition에 관련한 View들의 SuperView입니다. 

위 사진으로 이해하면 잘 이해할 수 있습니다.

필요한 세개의 구성성분을 잘 선언했으니 사용해야 합니다.

 

 

 

 

Old View는 새로운 뷰가 나타남에 따라 사라지는 효과를 만들어 낼 것입니다.

아래는 animateTransition(using:):.에 선언해 줍니다.

containerView.addSubview(toView)
toView.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
UIView.animate(
  	withDuration: duration,
  	animations: {
    	toView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
  	},
  	completion: { _ in
    	transitionContext.completeTransition(true)
  	}
 )

 

 

CGAffineTransform(scaleX: ~ , y: ~)

입니다. 위는 저번 시간 포스팅에 잘 나와 있는데요, 원래 뷰의 크기를 대입된 가로(X) 나 세로(y)로 늘리거나 줄이는 방식입니다.