When you animate your transition correctly, viewDidAppear
will be called when the animation is done. See Customizing Transition Animations in View Controller Programming Guide for iOS for instructions on the proper way to customize a transition between two view controllers.
As that guide says, when you want to customize a modal transition, you should specify a modalPresentationStyle
of .custom
and then specify a transitioningDelegate
which will supply:
- Presentation controller;
- Animation controller for presenting a modal; and
- Animation controller for dismissing a modal
For example, the destination view controller would specify that it will do a custom transition:
class ViewController: UIViewController {
// if storyboards, override `init(coder:)`; if NIBs or programmatically
// created view controllers, override the appropriate `init` method.
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
transitioningDelegate = self
modalPresentationStyle = .custom
}
...
}
And, in its UIViewControllerTransitioningDelegate
, it vends that presentation controller and the animation controllers:
extension ViewController: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return TransitionAnimator(operation: .present)
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return TransitionAnimator(operation: .dismiss)
}
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return PresentationController(presentedViewController: presented, presenting: presenting)
}
}
All the presentation controller does is to specify that the presenter's view should be removed from the view hierarchy when the transition is done (which is the rule of thumb unless the presenting view is translucent or doesn't cover the whole screen):
class PresentationController: UIPresentationController {
override var shouldRemovePresentersView: Bool { return true }
}
And the animator specifies the duration and the particular details of the animation:
class TransitionAnimator: NSObject {
enum TransitionOperation {
case present
case dismiss
}
private let operation: TransitionOperation
init(operation: TransitionOperation) {
self.operation = operation
super.init()
}
}
extension TransitionAnimator: UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 2.0
}
func animateTransition(using context: UIViewControllerContextTransitioning) {
let toVC = context.viewController(forKey: .to)!
let fromVC = context.viewController(forKey: .from)!
let container = context.containerView
let frame = fromVC.view.frame
let rightFrame = CGRect(origin: CGPoint(x: frame.origin.x + frame.width, y: frame.origin.y), size: frame.size)
let leftFrame = CGRect(origin: CGPoint(x: frame.origin.x - frame.width, y: frame.origin.y), size: frame.size)
switch operation {
case .present:
toVC.view.frame = rightFrame
container.addSubview(toVC.view)
UIView.animate(withDuration: transitionDuration(using: context), animations: {
toVC.view.frame = frame
fromVC.view.frame = leftFrame
}, completion: { finished in
fromVC.view.frame = frame
context.completeTransition(!context.transitionWasCancelled)
})
case .dismiss:
toVC.view.frame = leftFrame
container.addSubview(toVC.view)
UIView.animate(withDuration: transitionDuration(using: context), animations: {
toVC.view.frame = frame
fromVC.view.frame = rightFrame
}, completion: { finished in
fromVC.view.frame = frame
context.completeTransition(!context.transitionWasCancelled)
})
}
}
}
Obviously, do whatever animation you want, but hopefully you get the basic idea. Bottom line, the destination view controller should specify its transitioningDelegate
and then you can just do a standard modal presentation (either via present
or show
or just a segue), and your transition animation will be customized and your destination's viewDidAppear
will be called when the animation is done.