I've noticed a change in the way bar tint color animates in ios 10. I've created a sample project outlining the change: Github: ios10BarTintDemo
Basically on ios 9 the barTintColor animates smoothly using [UIViewControllerTransitionCoordinator animateAlongsideTransition]
but on ios 10 the animations are much less smooth and when popping a view controller doesn't animate at all, I've tried adding [self.navigationController.navigationBar layoutIfNeeded]
as mentioned in some similar answers but this doesn't seem to have any effect when pushing/popping controllers.
UPDATE
I've tested in iOS 10.3 and I think the problem was fixed. And transitionCordinator
is no need anymore. I think the animation is smooth. Please check my project on github or look at this code:
class ViewControllerA: UIViewController {
override func loadView() {
super.loadView()
title = "A"
view.backgroundColor = .white
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "NEXT", style: .plain, target: self, action: #selector(self.showController))
}
override func viewWillAppear(_ animated: Bool) {
setColors()
super.viewWillAppear(animated)
}
func showController() {
navigationController?.pushViewController(ViewControllerB(), animated: true)
}
private func setColors() {
navigationController?.navigationBar.tintColor = .black
navigationController?.navigationBar.barTintColor = .red
navigationController?.navigationBar.isTranslucent = false
}
}
class ViewControllerB: UIViewController {
override func loadView() {
super.loadView()
title = "B"
view.backgroundColor = .white
}
override func viewWillAppear(_ animated: Bool) {
setColors()
super.viewWillAppear(animated)
}
override func willMove(toParentViewController parent: UIViewController?) {
if parent == nil {
navigationController?.navigationBar.barTintColor = .red
}
super.willMove(toParentViewController: parent)
}
private func setColors() {
navigationController?.navigationBar.tintColor = .black
navigationController?.navigationBar.barTintColor = .blue
navigationController?.navigationBar.isTranslucent = false
}
}
============================================================================================================================================================================================================================================================================================================
To achieve this kind of animation you should use UIViewControllerTransitionCoordinator
as Apple documentation say it is :
An object that adopts the UIViewControllerTransitionCoordinator protocol provides support for animations associated with a view controller transition.(...)
So every UIViewController
has own transitionController
. To get this you should call in the UIViewControllerClass
:
self.transitionCoordinator()
From documentation:
Returns the active transition coordinator object.
So to get the result that you want you should implement animateAlongsideTransition
method in viewController transitionCoordinatior. Animation works when you click backButton
and swipe to back.
Example :
First Controller :
class ViewControllerA: UIViewController {
override func loadView() {
super.loadView()
title = "A"
view.backgroundColor = .white
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "NEXT", style: .plain, target: self, action: #selector(self.showController))
setColors()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
animate()
}
func showController() {
navigationController?.pushViewController(ViewControllerB(), animated: true)
}
private func animate() {
guard let coordinator = self.transitionCoordinator else {
return
}
coordinator.animate(alongsideTransition: {
[weak self] context in
self?.setColors()
}, completion: nil)
}
private func setColors() {
navigationController?.navigationBar.tintColor = .black
navigationController?.navigationBar.barTintColor = .red
}
}
Second Controller:
class ViewControllerB : UIViewController {
override func loadView() {
super.loadView()
title = "B"
view.backgroundColor = .white
setColors()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
animate()
}
override func willMove(toParentViewController parent: UIViewController?) { // tricky part in iOS 10
navigationController?.navigationBar.barTintColor = .red //previous color
super.willMove(toParentViewController: parent)
}
override func viewDidAppear(_ animated: Bool) {
navigationController?.navigationBar.barTintColor = .blue
}
private func animate() {
guard let coordinator = self.transitionCoordinator else {
return
}
coordinator.animate(alongsideTransition: {
[weak self] context in
self?.setColors()
}, completion: nil)
}
private func setColors(){
navigationController?.navigationBar.tintColor = .black
navigationController?.navigationBar.barTintColor = .blue
}
}
UPDATE iOS 10
In the iOS 10 the tricky part is to add the willMoveTo(parentViewController parent: UIViewController?)
in the second ViewController. And set the navigationBar tintColor
to the color value of previous controller. Also, in viewDidAppear
method in second ViewControler set the navigationBar.tintColor
to the color from second viewController.
Check out my example project on github
You can fix this popping issue by adding something similar to this, running it in viewWillDisappear won't work for some reason in iOS10
override func willMove(toParentViewController parent: UIViewController?) {
self.navigationController?.navigationBar.barTintColor = UIColor.red
super.willMove(toParentViewController: parent)
}