I have created a small project to replicate this problem.
The only file is this one...
class RootViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
showBlue()
}
@objc func showBlue() {
let vc = UIViewController()
vc.view.backgroundColor = .blue
let nvc = UINavigationController(rootViewController: vc)
vc.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(showGreen))
transition(to: nvc)
}
@objc func showGreen() {
let vc = UIViewController()
vc.view.backgroundColor = .green
let nvc = UINavigationController(rootViewController: vc)
vc.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(showBlue))
transition(to: nvc)
}
func transition(to toVC: UIViewController) {
if let fromVC = children.first {
transitionWithAnimation(fromVC: fromVC, toVC: toVC)
} else {
addWithoutAnimation(child: toVC)
}
}
func addWithoutAnimation(child toVC: UIViewController) {
addChild(toVC)
view.addSubview(toVC.view)
toVC.view.frame = view.bounds
toVC.didMove(toParent: self)
}
func transitionWithAnimation(fromVC: UIViewController, toVC: UIViewController) {
addChild(toVC)
toVC.view.frame = view.bounds
fromVC.willMove(toParent: nil)
transition(
from: fromVC,
to: toVC,
duration: 1.0,
options: .transitionCrossDissolve,
animations: nil) { _ in
fromVC.removeFromParent()
toVC.didMove(toParent: self)
}
}
}
The RootViewController first does showBlue
. This adds a child UINavigationController
with a rootViewController
with a blue background. The blue view controller has a Done
button that then targets showGreen
.
showGreen
transitions to a UINavigationController
with a green background and a Done
button that targets showBlue
.
What I expected (and what I want to happen) is for the navigation bar to cross dissolve in place without resizing.
The problem is that during the animated transition the navigation bar has a strange animation to it. Which you can see here...
All the code is followed exactly from the Apple documentation about adding child view controllers to a custom container view controller... https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html
I have also tried by using AutoLayout constraints rather than setting the view's frame directly but this didn't change anything.
I've tried running view.setNeedsLayout
and then view.layoutIfNeeded()
on the new view controller's view
but that doesn't seem to have fixed it either.
The really odd thing is that if you use any other type of view controller (other than UINavigationController
) then this animation glitch doesn't happen. For example: If one of the view controllers is a UITabBarController
then the tabs do not have this odd animation. Even stranger, if the tab contains a UINavigationController
then it doesn't have this animation either. It's literally just if the direct child is a UINavigationController
.
Has anyone experienced this before? And did you manage to stop the strange animation?
If you place the transition code within a CATransaction
and use the kCATransactionDisableActions
key to turn off implicit actions it will resolve the issue:
CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey:kCATransactionDisableActions)
transition(
from: fromVC,
to: toVC,
duration: 1.0,
options: [.transitionCrossDissolve],
animations: nil) { _ in
fromVC.removeFromParent()
toVC.didMove(toParent: self)
}
CATransaction.commit()