transitioningDelegate
- 모든 뷰 컨트롤러가 가질 수 있으며, UIViewControllerTransitioningDelegate 프로토콜을 따른다.
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning?
- 위 메소드를 구현하여 애니메이션 컨트롤러를 리턴하게 한다.
UIViewControllerAnimatedTransitioning
- 애니메이션 컨트롤러 라 칭한다.
- TransitioningDelegate 가 리턴하는 애니메이션 컨트롤러 객체
- transitionDuration 을 구현하여 애니메이션 실행 시간을 리턴한다.
- animateTransition 을 구현하여 fromController -> toController로 트랜지션 시 애니메이션을 직접 정의한다.
TransitioningContext
- UIViewControllerContextTransitioning 프로토콜을 따른다.
- 직접 만드는 것이 아닌, 각각 transition이 발생시 마다 UIKit이 생성해서 AnimationController로 전달해준다.
- 애니메이션 컨트롤러의 animateTransition 메소드의 파라미터로 받는다.
트랜지션 프로세스
- 트랜지션이 발생한다.
- UIKit은 To(보여질) 컨트롤러에게 TransitioningDelegate를 요청한다. 없으면 스탠다드 딜리게이트를 사용한다.
- UIKit은 TransitioningDelegate에게 애니메이션 컨트롤러를 요청한다. 만약 nil을 리턴한다면, 디폴트 애니메이션을 사용한다.
- animationController(forPresented:presenting:source:) 을 통해 요청
- UIKit은 TransitioningContext를 생성한다.
- UIKit은 애니메이션 컨트롤러에게 애니메이션 지속 시간을 요청한다.
- transitionDuration(using:)
- UIKit은 애니메이션 컨트롤러에 있는 animateTransition(using:) 을 호출하여 애니메이션을 실행한다.
- 애니메이션컨트롤러는 TransitionContext의 completeTransition(_:) 를 호출하여 애니메이션이 종료됨을 알린다.
사라지는 트랜지션은 To컨트롤러가 아닌, From컨트롤러를 호출하고(forDismissed), 나머지는 거의 일치한다.
(To -> From 으로 트랜지션 발생하므로, 그에 맞게 애니메이션도 정의 해주어야 한다.)
예시
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromVC = transitionContext.viewController(forKey: .from),
let toVC = transitionContext.viewController(forKey: .to),
let snapshot = toVC.view.snapshotView(afterScreenUpdates: true)
else {
return
}
let containerView = transitionContext.containerView
let finalFrame = transitionContext.finalFrame(for: toVC)
snapshot.frame = originFrame
snapshot.layer.cornerRadius = CardViewController.cardCornerRadius
snapshot.layer.masksToBounds = true
containerView.addSubview(toVC.view)
containerView.addSubview(snapshot)
toVC.view.isHidden = true
AnimationHelper.perspectiveTransform(for: containerView)
snapshot.layer.transform = AnimationHelper.yRotation(.pi / 2)
let duration = transitionDuration(using: transitionContext)
//키프레임 기반으로 애니메이션 설정
UIView.animateKeyframes(
withDuration: duration, //전체 애니메이션 지속 시간
delay: 0, // 애니메이션 시작 전 대기 딜레이
options: .calculationModeCubic, //애니메이션을 어떻게 수행할지에 대한 상수
animations: { //애니메이션 실제 구현
//withRelativeStartTime, relativeDuration 은 전부 비율이다.
//animateKeyframes 설정시 duration에 대한 비율대로 실행된다.
UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 1/3) {
fromVC.view.layer.transform = AnimationHelper.yRotation(-.pi / 2)
}
UIView.addKeyframe(withRelativeStartTime: 1/3, relativeDuration: 1/3) {
snapshot.layer.transform = AnimationHelper.yRotation(0.0)
}
UIView.addKeyframe(withRelativeStartTime: 2/3, relativeDuration: 1/3) {
snapshot.frame = finalFrame
snapshot.layer.cornerRadius = 0
}
}, completion: { _ in
//애니메이션 완료 시 핸들러
toVC.view.isHidden = false
snapshot.removeFromSuperview()
fromVC.view.layer.transform = CATransform3DIdentity
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
참조
https://www.kodeco.com/322-custom-uiviewcontroller-transitions-getting-started
'IOS' 카테고리의 다른 글
Swift enum (0) | 2022.12.31 |
---|---|
Swift 에러 핸들링 (try, catch, throw, throws, do) (0) | 2022.12.28 |
SwiftUI NavigationView BackButton Custom (0) | 2022.11.05 |
SwiftUI 에서 UIKit 사용하기 (0) | 2022.11.01 |
NSCollectionLayoutDimension 알아보기 (0) | 2022.10.15 |