The currently accepted answer (at the time of writing this answer) isn't ideal because it might not position UI elements like the navigation bar correctly during the animation or lead to other layout glitches as described in Marko Nikolovski's answer. However, pushing a snapshot of the current view controller's view on top of the next view controller's view for animation purposes isn't ideal either because it cannot be used for view controllers in which any kind of animation is happening during the transition to the next view controller as a snapshot is just a static image.
Taking cclogg's basic idea here's my implementation for a smooth transition between two view controllers that doesn't bother about alpha values:
/// Replaces the window's current `rootViewController` with the `newViewController`
/// passed as a parameter. If `animated`, the window animates the replacement
/// with a cross dissolve transition.
///
/// - Parameters:
/// - newViewController: The view controller that replaces the current view controller.
/// - animated: Specifies if the transition between the two view controllers is animated.
/// - duration: The transition's duration. (Ignored if `animated == false`)
private func swapCurrentViewController(with newViewController: UIViewController,
animated: Bool = true,
duration: TimeInterval = 1) {
// Get a reference to the window's current `rootViewController` (the "old" one)
let oldViewController = window?.rootViewController
// Replace the window's `rootViewController` with the new one
window?.rootViewController = newViewController
if animated, let oldView = oldViewController?.view {
// Add the old view controller's view on top of the new `rootViewController`
newViewController.view.addSubview(oldView)
// Remove the old view controller's view in an animated fashion
UIView.transition(with: window!,
duration: duration,
options: .transitionCrossDissolve,
animations: { oldView.removeFromSuperview() },
completion: nil)
}
}
It's important to replace the window's rootViewController
before initiating the transition or animation because that's how the new view controller gets to know its context, i.e. the correct layout margins etc.
This is a way to go if you really need to replace the window's rootViewController
for whatever reason. However, I want to point out that from an architectural point of view a far better approach in my opinion is to create a view controller that's solely responsible for handling transitions between the two other view controllers and set that one as the window's rootViewController
and then never change it. It's well explained in this answer to a similar question.