ios - life - view controller que es
¿Cómo hago la transición/animar el color de UINavigationBar? (2)
He estado buscando cómo hacer la transición / animación del barTintColor
de UINavigationBar
por un tiempo, y solo veo diferentes respuestas. Algunos usan UIView.animateWithDuration
, algunos usan CATransition
, pero los más interesantes, como este, usan animate(alongsideTransition animation..
CATransition
animate(alongsideTransition animation..
, a la que me gusta el sonido, pero no puedo hacer que funcione correctamente. ¿Estoy haciendo algo mal?
Muchos especifican que simplemente puedo usar transitionCoordinator
en viewWillAppear:
He creado un nuevo proyecto súper pequeño como este:
class RootViewController:UIViewController{ //Only subclassed
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
transitionCoordinator?.animate(alongsideTransition: { [weak self](context) in
self?.setNavigationColors()
}, completion: nil)
}
func setNavigationColors(){
//Override in subclasses
}
}
class FirstViewController: RootViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "First"
}
override func setNavigationColors(){
navigationController?.navigationBar.barTintColor = UIColor.white
navigationController?.navigationBar.tintColor = UIColor.black
navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.black]
navigationController?.navigationBar.barStyle = UIBarStyle.default
}
}
class SecondViewController: RootViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Second"
}
override func setNavigationColors(){
navigationController?.navigationBar.barTintColor = UIColor.black
navigationController?.navigationBar.tintColor = UIColor.white
navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
navigationController?.navigationBar.barStyle = UIBarStyle.black
}
}
- La transición de empuje de
First
aSecond
parece perfecta. Todos los elementos cambian perfectamente, tal vez excepto StatusBar, que cambia instantáneamente a blanco. Prefiero saber cómo hacer la transición, pero lo aceptaré por ahora. - La transición de
Second
aFirst
está completamente equivocada. Mantiene los colores deSecond
hasta que la transición esté completa. - La transición de arrastre del
Second
alFirst
ve bien, al arrastrar todo el camino. Una vez más, el StatusBar se vuelve negro inmediatamente tan pronto como empiezo a arrastrar, pero no sé si eso es posible solucionarlo. - La transición de arrastre del
Second
alFirst
pero cancelada a mitad del arrastre y volviendo alSecond
está completamente arruinada. Se ve bien hasta queSecond
recupera completamente el control, y luego cambia de repente aFirst
-colors. Esto no debería suceder.
Hice algunos cambios en mi RootViewController
para hacerlo un poco mejor. viewWillAppear:
completamente y lo cambié con esto:
class RootViewController:UIViewController{
override func willMove(toParentViewController parent: UIViewController?) {
if let last = self.navigationController?.viewControllers.last as? RootViewController{
if last == self && self.navigationController!.viewControllers.count > 1{
if let parent = self.navigationController!.viewControllers[self.navigationController!.viewControllers.count - 2] as? RootViewController{
parent.setNavigationColors()
}
}
}
}
override func viewWillDisappear(_ animated: Bool) {
if let parent = navigationController?.viewControllers.last as? RootViewController{
parent.animateNavigationColors()
}
}
override func viewDidAppear(_ animated: Bool) {
self.setNavigationColors()
}
func animateNavigationColors(){
transitionCoordinator?.animate(alongsideTransition: { [weak self](context) in
self?.setNavigationColors()
}, completion: nil)
}
func setNavigationColors(){
//Override in subclasses
}
}
Con este código actualizado, obtengo esto:
Algunas observaciones:
- La transición de
First
aSecond
es la misma - La transición de pop de
Second
aFirst
ahora se está animando correctamente, excepto desde la flecha de retroceso, el texto de fondo (y la barra de estado, pero sí ...). Estos se cambian al instante a negro. En el primer gif, se podía ver que la flecha hacia atrás y el texto hacia atrás también transitaban. - La transición de arrastre de la
Second
a laFirst
también tiene este problema, la flecha de retroceso y el texto de atrás repentinamente son instantáneamente negros al comenzar. El barTint es fijo para que no tenga un color incorrecto al cancelar el arrastre.
¿Qué estoy haciendo mal? ¿Cómo se supone que haga esto?
Lo que quiero es hacer la transición de todos los elementos sin problemas. El tinte del botón de retroceso, el texto de fondo, el título, barTint y la barra de estado. ¿No es esto posible?
Puede sobrescribir los métodos push y pop de UINavigationController
para establecer el color de la barra. He almacenado el color de la barra correspondiente a un controlador de vista en su elemento de navegación con una subclase personalizada de UINavigationItem
. El siguiente código funciona para mí en iOS 11 para transiciones completas y también para interactivas:
import UIKit
class NavigationItem: UINavigationItem {
@IBInspectable public var barTintColor: UIColor?
}
class NavigationController: UINavigationController, UIGestureRecognizerDelegate {
func applyTint(_ navigationItem: UINavigationItem?) {
if let item = navigationItem as? NavigationItem {
self.navigationBar.barTintColor = item.barTintColor
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
applyTint(self.topViewController?.navigationItem)
self.interactivePopGestureRecognizer?.delegate = self
}
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
applyTint(viewController.navigationItem)
super.pushViewController(viewController, animated: animated)
}
override func popViewController(animated: Bool) -> UIViewController? {
let viewController = super.popViewController(animated: animated)
applyTint(self.topViewController?.navigationItem)
return viewController
}
override func popToViewController(_ viewController: UIViewController, animated: Bool) -> [UIViewController]? {
let result = super.popToViewController(viewController, animated: animated)
applyTint(viewController.navigationItem)
return result
}
override func popToRootViewController(animated: Bool) -> [UIViewController]? {
let result = super.popToRootViewController(animated: animated)
applyTint(self.topViewController?.navigationItem)
return result
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return (otherGestureRecognizer is UIScreenEdgePanGestureRecognizer)
}
}
Nota: La coordinación de la animación de color se realiza por el controlador de navegación
en 10 iOS funciona imperfectamente :(
Subclase su controlador de navegación para usar el estilo de barra de estado del controlador de vista visible:
class MyNavigationController: UINavigationController {
override var preferredStatusBarStyle: UIStatusBarStyle {
return visibleViewController!.preferredStatusBarStyle
}
}
Anule preferredStatusBarStyle en el controlador raíz y agregue la función para establecer los estilos antes de la animación pop:
private var _preferredStyle = UIStatusBarStyle.default;
override var preferredStatusBarStyle: UIStatusBarStyle {
get {
return _preferredStyle
}
set {
_preferredStyle = newValue
self.setNeedsStatusBarAppearanceUpdate()
}
}
func animateNavigationColors(){
self.setBeforePopNavigationColors()
transitionCoordinator?.animate(alongsideTransition: { [weak self](context) in
self?.setNavigationColors()
}, completion: nil)
}
func setBeforePopNavigationColors() {
//Override in subclasses
}
En primer controlador:
override func setBeforePopNavigationColors() {
navigationController?.navigationBar.tintColor = UIColor.white
navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
self.preferredStatusBarStyle = UIStatusBarStyle.lightContent
}
override func setNavigationColors(){
navigationController?.navigationBar.barTintColor = UIColor.white
navigationController?.navigationBar.tintColor = UIColor.black
navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.black]
navigationController?.navigationBar.barStyle = UIBarStyle.default
self.preferredStatusBarStyle = UIStatusBarStyle.default
}
En segundo:
override func setNavigationColors(){
navigationController?.navigationBar.barTintColor = UIColor.black
navigationController?.navigationBar.tintColor = UIColor.white
navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
navigationController?.navigationBar.barStyle = UIBarStyle.black
self.preferredStatusBarStyle = UIStatusBarStyle.lightContent
}
Proyecto de ejemplo: https://github.com/josshad/TestNavBarTransition