Alcanzar colores vivos y shinys para una UINavigationBar translúcida iOS 7


ACTUALIZACIÓN DE iOS 7.1 : Parece que la solución para modificar el canal alfa en UINavigationBar se ha ignorado en esta actualización. En este momento, la mejor solución parece ser ‘lidiar con eso’ y esperar que cualquier color que elija pueda tener un efecto translúcido. Todavía estoy buscando formas de evitar esto.


ACTUALIZACIÓN DE iOS 7.0.3 : La biblioteca de GitHub que creamos se ha actualizado para solucionar este problema al usar iOS 7.0.3. Desafortunadamente, no existe una fórmula mágica para admitir ambos colores creados en iOS 7.0.2 y versiones anteriores e iOS 7.0.3. Parece que Apple mejoró la saturación, pero a costa de la opacidad (ya que la translucidez borrosa depende del nivel de opacidad). Yo, junto con algunos otros, estamos trabajando para crear una solución mucho mejor para esto.


Estoy seguro de que muchas personas ya han encontrado el problema donde iOS 7 tiende a desaturar el color de una UINavigationBar que es translúcida.

Mi objective es lograr un UINavigationBar con este color de tinte, pero translúcido:

UINavigationBar, opaco

Sin embargo, con translucidez, estoy obteniendo esto. La vista de fondo es blanca, lo cual entiendo que hará que esta vista sea un poco más clara:

UINavigationBar, translúcido

¿Hay alguna manera de lograr el color original sin dejar de tener translucidez? Me di cuenta de que Facebook ha podido conseguir que su barra sea su color azul intenso, como se muestra aquí:

Facebook UINavigationBar, translúcido

..so sé que tiene que haber alguna forma. Las vistas de fondo obviamente hacen una diferencia aquí, pero la mayor parte de su contenido también es gris / blanco. Parece que independientemente del color de tinte de barra que pongas, no puedes obtener colores vivos bajo translucidez.

Actualizado con la solución

Aquí está la solución con la que terminé. Tomé la solución de aprato y luego UINavigationBar el UINavigationBar personalizado dentro de una subclase UINavigationController . Creé un repository que incluye esta implementación, junto con una aplicación de ejemplo .

 //////////////////////////// // CRNavigationBar.m //////////////////////////// #import "CRNavigationBar.h" @interface CRNavigationBar () @property (nonatomic, strong) CALayer *colorLayer; @end @implementation CRNavigationBar static CGFloat const kDefaultColorLayerOpacity = 0.5f; static CGFloat const kSpaceToCoverStatusBars = 20.0f; - (void)setBarTintColor:(UIColor *)barTintColor { [super setBarTintColor:barTintColor]; if (self.colorLayer == nil) { self.colorLayer = [CALayer layer]; self.colorLayer.opacity = kDefaultColorLayerOpacity; [self.layer addSublayer:self.colorLayer]; } self.colorLayer.backgroundColor = barTintColor.CGColor; } - (void)layoutSubviews { [super layoutSubviews]; if (self.colorLayer != nil) { self.colorLayer.frame = CGRectMake(0, 0 - kSpaceToCoverStatusBars, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds) + kSpaceToCoverStatusBars); [self.layer insertSublayer:self.colorLayer atIndex:1]; } } @end 

 //////////////////////////// // CRNavigationController.m //////////////////////////// #import "CRNavigationController.h" #import "CRNavigationBar.h" @interface CRNavigationController () @end @implementation CRNavigationController - (id)init { self = [super initWithNavigationBarClass:[CRNavigationBar class] toolbarClass:nil]; if(self) { // Custom initialization here, if needed. } return self; } - (id)initWithRootViewController:(UIViewController *)rootViewController { self = [super initWithNavigationBarClass:[CRNavigationBar class] toolbarClass:nil]; if(self) { self.viewControllers = @[rootViewController]; } return self; } @end 

ACTUALIZACIÓN DE iOS 7.0.3: Como ve arriba, 7.0.3 cambió las cosas. He actualizado mi esencia. Con suerte, esto desaparecerá a medida que las personas mejoren.

Respuesta original: Terminé con un truco combinando las otras dos respuestas. Estoy subclasificando UINavigationBar y agregando una capa en la parte posterior con algo de espacio extra para cubrir si alguna de las diversas barras de estado de altura están activas. La capa se ajusta en las subvistas de diseño y el color cambia siempre que establezca barTintColor.

Gist: https://gist.github.com/aprato/6631390

setBarTintColor

  [super setBarTintColor:barTintColor]; if (self.extraColorLayer == nil) { self.extraColorLayer = [CALayer layer]; self.extraColorLayer.opacity = self.extraColorLayerOpacity; [self.layer addSublayer:self.extraColorLayer]; } self.extraColorLayer.backgroundColor = barTintColor.CGColor; 

layoutSubviews

  [super layoutSubviews]; if (self.extraColorLayer != nil) { [self.extraColorLayer removeFromSuperlayer]; self.extraColorLayer.opacity = self.extraColorLayerOpacity; [self.layer insertSublayer:self.extraColorLayer atIndex:1]; CGFloat spaceAboveBar = self.frame.origin.y; self.extraColorLayer.frame = CGRectMake(0, 0 - spaceAboveBar, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds) + spaceAboveBar); } 

El comportamiento de tintColor para barras ha cambiado en iOS 7.0. Ya no afecta el fondo de la barra y se comporta como se describe para la propiedad tintColor agregada a UIView. Para matizar los antecedentes del compás, utilice -barTintColor.Puede usar el siguiente código para que la aplicación funcione tanto con iOS6 como con iOS7.

 if(IS_IOS7) { self.navigationController.navigationBar.barTintColor = [UIColor blackColor]; self.navigationController.navigationBar.translucent = NO; } else { self.navigationController.navigationBar.tintColor = [UIColor blackColor]; } 

IS_IOS7 es una macro que se define en el archivo pch de la siguiente manera.

 #define IS_IOS7 ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0) 

No se me ocurrió esta solución, pero parece funcionar bastante bien. Acabo de agregarlo a viewDidLoad en mi subclase de UINavigationController.

Fuente: https://gist.github.com/alanzeino/6619253

 // cheers to @stroughtonsmith for helping out with this one UIColor *barColour = [UIColor colorWithRed:0.13f green:0.14f blue:0.15f alpha:1.00f]; UIView *colourView = [[UIView alloc] initWithFrame:CGRectMake(0.f, -20.f, 320.f, 64.f)]; colourView.opaque = NO; colourView.alpha = .7f; colourView.backgroundColor = barColour; self.navigationBar.barTintColor = barColour; [self.navigationBar.layer insertSublayer:colourView.layer atIndex:1]; 

Una forma de baja fidelidad probablemente UIView una UIView que es la altura de la barra de navegación en la parte superior de la vista detrás de la barra. Haga que esa vista tenga el mismo color que la barra de navegación pero juegue con el alfa hasta que obtenga los efectos deseados:

 UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.navigationController.navigationBar.frame), 64)]; backgroundView.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:1 alpha:.5]; [self.navigationController.view insertSubview:backgroundView belowSubview:self.navigationController.navigationBar]; 

UIView detrás

enter image description here

(Se cambió el color de los ejemplos más bajos a la transparencia de énfasis. La transparencia / el desenfoque es más notorio cuando se está en movimiento).

UINavigationBar el UINavigationBar y colocar esa misma vista por encima del fondo, pero detrás de todo lo demás, probablemente logre resultados similares aunque sea menos hacky.


Otra solución que he visto es jugar con el alfa de UINavigationBar :

 self.navigationController.navigationBar.alpha = 0.5f; 

Editar: en realidad, después de las pruebas parece que esto no proporciona el comportamiento previsto (ni ningún comportamiento):

.8 alfa

Barra de navegación con .8 alpha

Alfa sin ajustar

enter image description here

Obviamente, solo querrás hacer esto en dispositivos iOS 7. Por lo tanto, agregue algún control de versión antes de implementar cualquiera de estos.

En lugar de crear su objeto UIColor en formato RGB , use HSB y aumente el parámetro de saturación. (Créditos a Sam Soffes quien describe este método aquí )

 navigationBar.barTintColor = [UIColor colorWithHue:0.555f saturation:1.f brightness:0.855f alpha:1.f]; 

Nota: Esta solución es una compensación y no funciona bien para colores con alta saturación.

Para elegir el color HSB de su diseño, puede usar una herramienta como ColorSnapper que le permite simplemente copiar el formato UIColor HSB.

También puede probar la Categoría UIColor ( GitHub Link ) de David Keegan para modificar los colores existentes.

El problema ahora ha sido solucionado por Apple en la nueva versión 7.0.3.

Utilicé la solución de @ aprato pero encontré algunos casos de esquina donde las nuevas capas de nuevos VC (por ejemplo, UINavigationItemButtonViews , UINavigationItemViews , etc.) se insertarían automáticamente en una posición debajo del extraColorLayer (lo que causaría que esos elementos de título o botón se vean afectados por el extraColorLayer y por lo tanto, de color más débil de lo que normalmente sería). Así que ajusté la solución de @ aprato para forzar que el extraColorLayer permanezca en la posición de índice 1. En la posición de índice 1, el extraColorLayer permanece justo encima del _UINavigationBarBackground , pero debajo de todo lo demás.

Aquí está mi implementación de clase:

 - (void)setBarTintColor:(UIColor *)barTintColor { [super setBarTintColor:barTintColor]; if (self.extraColorLayer == nil) { self.extraColorLayer = [CALayer layer]; self.extraColorLayer.opacity = kDefaultColorLayerOpacity; [self.layer insertSublayer:self.extraColorLayer atIndex:1]; // This way the text comes out clear } self.extraColorLayer.backgroundColor = barTintColor.CGColor; } - (void)layoutSubviews { [super layoutSubviews]; if (self.extraColorLayer != nil) { self.extraColorLayer.frame = CGRectMake(0, 0 - kSpaceToCoverStatusBars, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds) + kSpaceToCoverStatusBars); } } - (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview { [super insertSubview:view aboveSubview:siblingSubview]; [self.extraColorLayer removeFromSuperlayer]; [self.layer insertSublayer:self.extraColorLayer atIndex:1]; // This way the text comes out clear } - (void)insertSubview:(UIView *)view atIndex:(NSInteger)index { [super insertSubview:view atIndex:index]; [self.extraColorLayer removeFromSuperlayer]; [self.layer insertSublayer:self.extraColorLayer atIndex:1]; // This way the text comes out clear } - (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview { [super insertSubview:view belowSubview:siblingSubview]; [self.extraColorLayer removeFromSuperlayer]; [self.layer insertSublayer:self.extraColorLayer atIndex:1]; // This way the text comes out clear } 

He mejorado tu código en mi fork: https://github.com/allenhsu/CRNavigationController

Con mi modificación, el color del resultado en la pantalla (recogido sobre fondo blanco) será exactamente el mismo valor pasado a setBarTintColor . Creo que es una solución increíble.

Ninguno de estos hacks son necesarios :). Simplemente establecido:

 self.navigationController.navigationBar.translucent = NO; 

Para iOS 7, la translucidez predeterminada se ha mantenido en VERDADERO.


En una nota relacionada, puede establecer el color del texto del título (con sombra) fácilmente a través de:

 NSShadow *titleShadow = [[NSShadow alloc] init]; titleShadow.shadowOffset = CGSizeMake(0.0f, -1.0f); titleShadow.shadowColor = [UIColor blackColor]; NSDictionary *navbarTitleTextAttributes = @{NSForegroundColorAttributeName: [UIColor whiteColor], NSShadowAttributeName: titleShadow}; [[UINavigationBar appearance] setTitleTextAttributes:navbarTitleTextAttributes]; 

Me encontré con este Q / A al intentar configurar una barra de navegación de color uniforme con la transparencia DISABLED en iOS 7.

Después de experimentar un rato con barTintColor, descubrí que una forma muy fácil de tener una barra de navegación opaca es hacer una sola imagen de píxel del color deseado, crear una imagen estirable y establecerla en el fondo. Imagen de la barra de navegación .

 UIImage *singlePixelImage = [UIImage imageNamed:@"singlePixel.png"]; UIImage *resizableImage = [singlePixelImage resizableImageWithCapInsets:UIEdgeInsetsZero]; [navigationBar setBackgroundImage:resizableImage forBarMetrics:UIBarMetricsDefault]; 

Tres líneas de código, muy simple y funciona TANTO en iOS 6 como en iOS 7 (barTintColor no es compatible con iOS 6).

Hay un gran reemplazo Dropin UINavigationController disponible de Simon Booth disponible en GitHub Aquí GitHub – C360NavigationBar

Si está retrocediendo con iOS6, verifique el controlador de la vista raíz como tal:

PatientListTableViewController * frontViewController = [[PatientListTableViewController alloc] init];

  UINavigationController *navViewController = [[UINavigationController alloc] initWithNavigationBarClass:[C360NavigationBar class] toolbarClass:nil]; if ([navViewController.view respondsToSelector:@selector(setTintColor:)]) { //iOS7 [navViewController.view setTintColor:self.navBarTintColor]; [[C360NavigationBar appearance] setItemTintColor:self.navBarItemTintColor]; } else { //iOS6 [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque animated:NO]; navViewController.navigationBar.tintColor = self.navBarTintColor; } [navViewController pushViewController:frontViewController animated:NO]; self.window.rootViewController = navViewController; 

Como @bernhard mencionado anteriormente , es posible saturar el color del tinte de la barra para obtener la apariencia deseada de la barra de navegación.

Escribí una utilidad BarTintColorOptimizer para ese tipo de ajuste. Optimiza el color del tinte de la barra translúcida para que el color real de la barra coincida con el color deseado en iOS 7.xy posterior. Mire esta respuesta para más detalles.

Hablando francamente, las respuestas anteriores pueden ser correctas, pero seguir el truco funcionó para mí con mucha facilidad.

 // this is complete 100% transparent image self.imageBlack = [[UIImage imageNamed:@"0102_BlackNavBG"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 2, 0, 2) resizingMode:UIImageResizingModeStretch]; // this is non-transparent but iOS7 // will by default make it transparent (if translucent is set to YES) self.imageRed = [[UIImage imageNamed:@"0102_RedNavBG"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 2, 0, 2) resizingMode:UIImageResizingModeStretch]; // some navigation controller [nvCtrLeft.navigationBar setBackgroundImage:self.imageRed forBarMetrics:UIBarMetricsDefault]; // some another navigation controller [nvCtrCenter.navigationBar setBackgroundImage:self.imageRed forBarMetrics:UIBarMetricsDefault]; 

Aquí están las imágenes utilizadas para self.imageRed y self.imageBlack .

< self.imageBlack > la imagen negra está en este paréntesis no será visible ya que es transparente 🙂

< self.imageRed > la imagen roja está en este paréntesis.

¿Hay alguna manera de usar la solución @aprato sin subclasificar UINavigationBar?

En mi proyecto, mi vista principal es un UIViewController.

el problema es que el navigationController es una propiedad de solo lectura, ¿hay alguna manera de usar tu clase con mi proyecto porque no puedo usar: [[UINavigationController alloc] initWithNavigationBarClass:

Gracias

Una forma fácil de obtener el color que desea es usar

  [ setBackgroundImage: forBarPosition: barMetrics:]; 

Siempre que su imagen tenga algo de alfa, la translucidez funcionará y podrá configurar el alfa cambiando la imagen. Esto fue agregado en iOS7. El ancho y alto de la imagen son 640x88px para vertical (agregue 20 a 88 si desea que esté debajo de la barra de estado).