Seguí este hilo para anular -preferredStatusBarStyle
, pero no se llama. ¿Hay alguna opción que pueda cambiar para habilitarla? (Estoy usando XIB en mi proyecto).
Seguí este hilo para anular -preferredStatusBarStyle
, pero no se llama. ¿Hay alguna opción que pueda cambiar para habilitarla? (Estoy usando XIB en mi proyecto).
Respuestas:
Tuve el mismo problema y descubrí que estaba sucediendo porque no estaba configurando el controlador de vista raíz en la ventana de mi aplicación.
El UIViewController
en el que había implementado preferredStatusBarStyle
fue utilizado en un UITabBarController
, que controlaba la apariencia de las vistas en la pantalla.
Cuando configuré el controlador de vista raíz para que apunte a esto UITabBarController
, los cambios en la barra de estado comenzaron a funcionar correctamente, como se esperaba (y preferredStatusBarStyle
se estaba llamando al método).
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
... // other view controller loading/setup code
self.window.rootViewController = rootTabBarController;
[self.window makeKeyAndVisible];
return YES;
}
Alternativamente, puede llamar a uno de los siguientes métodos, según corresponda, en cada uno de sus controladores de vista, dependiendo de su color de fondo, en lugar de tener que usar setNeedsStatusBarAppearanceUpdate
:
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
o
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];
Tenga en cuenta que también tendrá que establecer UIViewControllerBasedStatusBarAppearance
que NO
en el archivo plist si se utiliza este método.
setNeedsStatusBarAppearanceUpdate
; mis sospechas se confirmaron cuando hice este cambio.
Para cualquiera que use un UINavigationController:
El UINavigationController
no reenvía las preferredStatusBarStyle
llamadas a sus controladores de vista secundarios. En cambio, administra su propio estado, como debería, está dibujando en la parte superior de la pantalla donde vive la barra de estado y, por lo tanto, debería ser responsable de ello. Por lo tanto, la implementación preferredStatusBarStyle
en sus VC dentro de un controlador de navegación no hará nada, nunca se los llamará.
El truco es lo que la UINavigationController
utiliza para decidir qué volver a UIStatusBarStyleDefault
o UIStatusBarStyleLightContent
. Basa esto en su UINavigationBar.barStyle
. El valor predeterminado ( UIBarStyleDefault
) da como resultado la UIStatusBarStyleDefault
barra de estado de primer plano oscuro . Y UIBarStyleBlack
le dará una UIStatusBarStyleLightContent
barra de estado.
TL; DR:
Si quieres UIStatusBarStyleLightContent
un UINavigationController
uso:
self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
preferredStatusBarStyle
en cuenta que , de hecho, se llamará al controlador de vista secundario si oculta la barra de navegación (establecida navigationBarHidden
en YES
), exactamente según corresponda.
[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack]
navigationBarHidden
conjunto a YES
realmente habrá preferredStatusBarStyle
llamado, y una advertencia a aquellos que podrían tropezar con esto: ¡funciona con navigationBarHidden
, pero no con navigationBar.hidden
!
Así que en realidad agregué una categoría a UINavigationController pero usé los métodos:
-(UIViewController *)childViewControllerForStatusBarStyle;
-(UIViewController *)childViewControllerForStatusBarHidden;
y los devolvió el UIViewController visible actual. Eso permite que el controlador de vista visible actual establezca su propio estilo / visibilidad preferida.
Aquí hay un fragmento de código completo para ello:
En Swift:
extension UINavigationController {
public override func childViewControllerForStatusBarHidden() -> UIViewController? {
return self.topViewController
}
public override func childViewControllerForStatusBarStyle() -> UIViewController? {
return self.topViewController
}
}
En el objetivo-C:
@interface UINavigationController (StatusBarStyle)
@end
@implementation UINavigationController (StatusBarStyle)
-(UIViewController *)childViewControllerForStatusBarStyle {
return self.topViewController;
}
-(UIViewController *)childViewControllerForStatusBarHidden {
return self.topViewController;
}
@end
Y por si acaso, así es como se implementa en un UIViewController:
En veloz
override public func preferredStatusBarStyle() -> UIStatusBarStyle {
return .LightContent
}
override func prefersStatusBarHidden() -> Bool {
return false
}
En el objetivo-C
-(UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleLightContent; // your own style
}
- (BOOL)prefersStatusBarHidden {
return NO; // your own visibility code
}
Finalmente, asegúrese de que su lista de aplicaciones NO tenga la opción "Ver la apariencia de la barra de estado basada en el controlador" en NO. ¿Eliminar esa línea o establecerla en SÍ (que creo que es el predeterminado ahora para iOS 7?)
return self.topViewController;
funciona para mí, pero return self.visibleViewController;
, no
super
a este método y realmente desea cambiar el comportamiento de todos los controladores de este tipo
Para cualquiera que todavía tenga dificultades con esto, esta simple extensión en Swift debería solucionar el problema por usted.
extension UINavigationController {
override open var childForStatusBarStyle: UIViewController? {
return self.topViewController
}
}
Mi aplicación utiliza los tres: UINavigationController
, UISplitViewController
, UITabBarController
, por tanto, todos éstos parecen tomar el control de la barra de estado y causarán preferedStatusBarStyle
a no ser llamado por sus hijos. Para anular este comportamiento, puede crear una extensión como el resto de las respuestas han mencionado. Aquí hay una extensión para los tres, en Swift 4. Desearía que Apple fuera más claro sobre este tipo de cosas.
extension UINavigationController {
open override var childViewControllerForStatusBarStyle: UIViewController? {
return self.topViewController
}
open override var childViewControllerForStatusBarHidden: UIViewController? {
return self.topViewController
}
}
extension UITabBarController {
open override var childViewControllerForStatusBarStyle: UIViewController? {
return self.childViewControllers.first
}
open override var childViewControllerForStatusBarHidden: UIViewController? {
return self.childViewControllers.first
}
}
extension UISplitViewController {
open override var childViewControllerForStatusBarStyle: UIViewController? {
return self.childViewControllers.first
}
open override var childViewControllerForStatusBarHidden: UIViewController? {
return self.childViewControllers.first
}
}
Editar: Actualización para cambios en la API de Swift 4.2
extension UINavigationController {
open override var childForStatusBarStyle: UIViewController? {
return self.topViewController
}
open override var childForStatusBarHidden: UIViewController? {
return self.topViewController
}
}
extension UITabBarController {
open override var childForStatusBarStyle: UIViewController? {
return self.children.first
}
open override var childForStatusBarHidden: UIViewController? {
return self.children.first
}
}
extension UISplitViewController {
open override var childForStatusBarStyle: UIViewController? {
return self.children.first
}
open override var childForStatusBarHidden: UIViewController? {
return self.children.first
}
}
La respuesta de Tyson es correcta para cambiar el color de la barra de estado a blanco UINavigationController
.
Si alguien quiere lograr el mismo resultado escribiendo el código AppDelegate
, use el siguiente código y escríbalo dentro del AppDelegate's
didFinishLaunchingWithOptions
método.
Y no se olvide de establecer el UIViewControllerBasedStatusBarAppearance
que YES
en el archivo plist, de lo contrario el cambio no se reflejará.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// status bar appearance code
[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];
return YES;
}
En un UINavigationController, preferredStatusBarStyle
no se llama porque topViewController
se prefiere self
. Entonces, para que te preferredStatusBarStyle
llamen en un UINavigationController, debes cambiarlo childViewControllerForStatusBarStyle
.
Anule su UINavigationController en su clase:
class MyRootNavigationController: UINavigationController {
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
override var childViewControllerForStatusBarStyle: UIViewController? {
return nil
}
}
Para hacerlo para todos los UINavigationController, puede anular una extensión (advertencia: afecta a UIDocumentPickerViewController, UIImagePickerController, etc.), pero probablemente no debería hacerlo de acuerdo con la documentación de Swift :
extension UINavigationController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
open override var childViewControllerForStatusBarStyle: UIViewController? {
return nil
}
}
Además de la respuesta de serenn, si presenta un controlador de vista con un modalPresentationStyle
(por ejemplo .overCurrentContext
), también debe llamar a esto en el controlador de vista recién presentado:
presentedViewController.modalPresentationCapturesStatusBarAppearance = true
No olvide anular también el preferredStatusBarStyle
controlador de vista presentado.
Una adición a la respuesta de Hippo: si está utilizando un UINavigationController, entonces probablemente sea mejor agregar una categoría:
// UINavigationController+StatusBarStyle.h:
@interface UINavigationController (StatusBarStyle)
@end
// UINavigationController+StatusBarStyle.m:
@implementation UINavigationController (StatusBarStyle)
- (UIStatusBarStyle)preferredStatusBarStyle
{
//also you may add any fancy condition-based code here
return UIStatusBarStyleLightContent;
}
@end
Esa solución es probablemente mejor que cambiar a un comportamiento que pronto será obsoleto.
preferredStatusBarStyle
y hace la lógica específica del UINavigationController. En este momento, esta lógica se basa, navigationBar.barStyle
pero puedo ver que se agregan verificaciones adicionales (por ejemplo, UISearchDisplayController
mover para ocultar el modo de barra de navegación). Al anular la lógica predeterminada, pierde toda esta funcionalidad y se deja abierto para los molestos momentos 'wtf' en el futuro. Consulte mi respuesta anterior para ver la forma correcta de hacer esto mientras aún admite el comportamiento del controlador de navegación incorporado.
Como se menciona en la respuesta seleccionada , la causa raíz es verificar el objeto del controlador de vista raíz de la ventana.
childForStatusBarStyle
Use las siguientes extensiones, maneja todos los escenarios anteriores :
extension UITabBarController {
open override var childForStatusBarStyle: UIViewController? {
return selectedViewController?.childForStatusBarStyle ?? selectedViewController
}
}
extension UINavigationController {
open override var childForStatusBarStyle: UIViewController? {
return topViewController?.childForStatusBarStyle ?? topViewController
}
}
extension AppRootViewController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
return children.first { $0.childForStatusBarStyle != nil }?.childForStatusBarStyle?.preferredStatusBarStyle ?? .default
}
}
UIViewControllerBasedStatusBarAppearance
clave info.plist
ya que es verdadera por defectoEn caso de que presente un nuevo flujo modalmente, se separa del flujo de estilo de la barra de estado existente. Por lo tanto, suponga que está presentando un NewFlowUIViewController
y luego agrega un nuevo controlador de navegación o tabBar a NewFlowUIViewController
, luego agrega una extensión NewFlowUIViewController
también para administrar el estilo de la barra de estado del controlador de vista adicional.
En caso de que establezca modalPresentationStyle que no sea fullScreen
mientras se presenta modalmente, debe establecerlo modalPresentationCapturesStatusBarAppearance
en verdadero para que el controlador de vista presentado deba recibir el control de apariencia de la barra de estado.
UINavigationController
es una subclase de UIViewController
(quién sabía 🙃)!
Por lo tanto, al presentar controladores de vista integrados en los controladores de navegación, en realidad no está presentando los controladores de vista integrados; ¡Estás presentando los controladores de navegación! UINavigationController
, como una subclase de UIViewController
, hereda preferredStatusBarStyle
y childForStatusBarStyle
, que puede establecer como desee.
Cualquiera de los siguientes métodos debería funcionar:
info.plist
, agregue la siguiente propiedad:
UIUserInterfaceStyle
(también conocido como "Estilo de interfaz de usuario")Anular preferredStatusBarStyle
dentroUINavigationController
preferredStatusBarStyle
( doc ): el estilo de barra de estado preferido para el controlador de vistaSubclase o extensión UINavigationController
class MyNavigationController: UINavigationController {
override var preferredStatusBarStyle: UIStatusBarStyle {
.lightContent
}
}
O
extension UINavigationController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
.lightContent
}
}
Anular childForStatusBarStyle
dentroUINavigationController
childForStatusBarStyle
( doc ): se llama cuando el sistema necesita el controlador de vista para determinar el estilo de la barra de estado"Si su controlador de vista de contenedor deriva su estilo de barra de estado de uno de sus controladores de vista secundarios, [anule esta propiedad] y devuelva ese controlador de vista secundario. Si devuelve nil o no anula este método, se usa el estilo de barra de estado para sí mismo . Si el valor de retorno de este método cambia, llame al método setNeedsStatusBarAppearanceUpdate () ".
Subclase o extensión UINavigationController
class MyNavigationController: UINavigationController {
override var childForStatusBarStyle: UIViewController? {
topViewController
}
}
O
extension UINavigationController {
open override var childForStatusBarStyle: UIViewController? {
topViewController
}
}
Puede devolver cualquier controlador de vista que desee arriba. Recomiendo uno de los siguientes:
topViewController
(of UINavigationController
) ( doc ): el controlador de vista en la parte superior de la pila de navegaciónvisibleViewController
(of UINavigationController
) ( doc ): el controlador de vista asociado con la vista actualmente visible en la interfaz de navegación (sugerencia: esto puede incluir "un controlador de vista que se presentó modalmente en la parte superior del controlador de navegación")Nota: Si decide subclase UINavigationController
, recuerde aplicar esa clase a sus controladores de navegación a través del inspector de identidad en IB.
PD Mi código usa la sintaxis Swift 5.1 😎
La respuesta de @ serenn anterior sigue siendo excelente para el caso de UINavigationControllers. Sin embargo, para swift 3, las funciones childViewController se han cambiado a vars
. Entonces el UINavigationController
código de extensión debe ser:
override open var childViewControllerForStatusBarStyle: UIViewController? {
return topViewController
}
override open var childViewControllerForStatusBarHidden: UIViewController? {
return topViewController
}
Y luego en el controlador de vista que debería dictar el estilo de la barra de estado:
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
Si su viewController está bajo UINavigationController.
Subclase UINavigationController y agregar
override var preferredStatusBarStyle: UIStatusBarStyle {
return topViewController?.preferredStatusBarStyle ?? .default
}
preferredStatusBarStyle
Se llamará a ViewController .
UIStatusBarStyle en iOS 7
La barra de estado en iOS 7 es transparente, la vista detrás se muestra.
El estilo de la barra de estado se refiere a las apariencias de su contenido. En iOS 7, el contenido de la barra de estado es oscuro ( UIStatusBarStyleDefault
) o claro ( UIStatusBarStyleLightContent
). Ambos UIStatusBarStyleBlackTranslucent
y UIStatusBarStyleBlackOpaque
están en desuso en iOS 7.0. UtilizarUIStatusBarStyleLightContent
lugar.
Como cambiar UIStatusBarStyle
Si debajo de la barra de estado hay una barra de navegación, el estilo de la barra de estado se ajustará para que coincida con el estilo de la barra de navegación (UINavigationBar.barStyle
):
Específicamente, si el estilo de la barra de navegación es UIBarStyleDefault, el estilo de la barra de estado será UIStatusBarStyleDefault
; si el estilo de la barra de navegación es UIBarStyleBlack
, el estilo de la barra de estado seráUIStatusBarStyleLightContent
.
Si no hay una barra de navegación debajo de la barra de estado, el estilo de la barra de estado puede ser controlado y cambiado por un controlador de vista individual mientras se ejecuta la aplicación.
- [UIViewController preferredStatusBarStyle]
es un nuevo método agregado en iOS 7. Se puede anular para devolver el estilo de barra de estado preferido:
- (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent;
}
Si el estilo de la barra de estado debe ser controlado por un controlador de vista secundario en lugar de uno mismo, anule -[UIViewController childViewControllerForStatusBarStyle]
para devolver ese controlador de vista secundario.
Si prefiere optar por este comportamiento y establecer el estilo de la barra de estado mediante el -[UIApplication statusBarStyle]
método, agregue la UIViewControllerBasedStatusBarAppearance
clave al Info.plist
archivo de una aplicación y dele el valor NO.
Si alguien está usando un controlador de navegación y quiere que todos sus controladores de navegación tengan el estilo negro, puede escribir una extensión para UINavigationController como esta en Swift 3 y se aplicará a todos los controladores de navegación (en lugar de asignarlo a un controlador en un hora).
extension UINavigationController {
override open func viewDidLoad() {
super.viewDidLoad()
self.navigationBar.barStyle = UIBarStyle.black
}
}
En Swift para cualquier tipo de UIViewController:
En tu AppDelegate
conjunto:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window!.rootViewController = myRootController
return true
}
myRootController
puede ser cualquier tipo de UIViewController
, por ejemplo, UITabBarController
o UINavigationController
.
Luego, anule este controlador raíz de esta manera:
class RootController: UIViewController {
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return .LightContent
}
}
Esto cambiará la apariencia de la barra de estado en toda su aplicación, porque el controlador raíz es el único responsable de la apariencia de la barra de estado.
Recuerde establecer la propiedad View controller-based status bar appearance
en SÍ en su Info.plist
para hacer que esto funcione (que es el valor predeterminado).
La mayoría de las respuestas no incluyen una buena implementación del childViewControllerForStatusBarStyle
método para UINavigationController
. Según mi experiencia, debe manejar casos como cuando se presenta un controlador de vista transparente sobre un controlador de navegación. En estos casos, debe pasar el control a su controlador modal ( visibleViewController
), pero no cuando está desapareciendo.
override var childViewControllerForStatusBarStyle: UIViewController? {
var childViewController = visibleViewController
if let controller = childViewController, controller.isBeingDismissed {
childViewController = topViewController
}
return childViewController?.childViewControllerForStatusBarStyle ?? childViewController
}
En cuanto a iOS 13.4 , no se llamará al preferredStatusBarStyle
método en la UINavigationController
categoría, swizzling parece ser la única opción sin la necesidad de usar una subclase.
Ejemplo:
Encabezado de categoría:
@interface UINavigationController (StatusBarStyle)
+ (void)setUseLightStatusBarStyle;
@end
Implementación:
#import "UINavigationController+StatusBarStyle.h"
#import <objc/runtime.h>
@implementation UINavigationController (StatusBarStyle)
void (^swizzle)(Class, SEL, SEL) = ^(Class c, SEL orig, SEL new){
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, new);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
};
+ (void)setUseLightStatusBarStyle {
swizzle(self.class, @selector(preferredStatusBarStyle), @selector(_light_preferredStatusBarStyle));
}
- (UIStatusBarStyle)_light_preferredStatusBarStyle {
return UIStatusBarStyleLightContent;
}
@end
Uso en AppDelegate.h:
#import "UINavigationController+StatusBarStyle.h"
[UINavigationController setUseLightStatusBarStyle];
Aquí está mi método para resolver esto.
Defina un protocolo llamado AGViewControllerAppearance .
AGViewControllerAppearance.h
#import <Foundation/Foundation.h>
@protocol AGViewControllerAppearance <NSObject>
@optional
- (BOOL)showsStatusBar;
- (BOOL)animatesStatusBarVisibility;
- (UIStatusBarStyle)preferredStatusBarStyle;
- (UIStatusBarAnimation)prefferedStatusBarAnimation;
@end
Defina una categoría en UIViewController llamada Upgrade .
UIViewController + Upgrade.h
#import <UIKit/UIKit.h>
@interface UIViewController (Upgrade)
//
// Replacements
//
- (void)upgradedViewWillAppear:(BOOL)animated;
@end
UIViewController + Upgrade.m
#import "UIViewController+Upgrade.h"
#import <objc/runtime.h>
#import "AGViewControllerAppearance.h" // This is the appearance protocol
@implementation UIViewController (Upgrade)
+ (void)load
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wselector"
Method viewWillAppear = class_getInstanceMethod(self, @selector(viewWillAppear:));
#pragma clang diagnostic pop
Method upgradedViewWillAppear = class_getInstanceMethod(self, @selector(upgradedViewWillAppear:));
method_exchangeImplementations(viewWillAppear, upgradedViewWillAppear);
}
#pragma mark - Implementation
- (void)upgradedViewWillAppear:(BOOL)animated
{
//
// Call the original message (it may be a little confusing that we're
// calling the 'same' method, but we're actually calling the original one :) )
//
[self upgradedViewWillAppear:animated];
//
// Implementation
//
if ([self conformsToProtocol:@protocol(AGViewControllerAppearance)])
{
UIViewController <AGViewControllerAppearance> *viewControllerConformingToAppearance =
(UIViewController <AGViewControllerAppearance> *)self;
//
// Status bar
//
if ([viewControllerConformingToAppearance respondsToSelector:@selector(preferredStatusBarStyle)])
{
BOOL shouldAnimate = YES;
if ([viewControllerConformingToAppearance respondsToSelector:@selector(animatesStatusBarVisibility)])
{
shouldAnimate = [viewControllerConformingToAppearance animatesStatusBarVisibility];
}
[[UIApplication sharedApplication] setStatusBarStyle:[viewControllerConformingToAppearance preferredStatusBarStyle]
animated:shouldAnimate];
}
if ([viewControllerConformingToAppearance respondsToSelector:@selector(showsStatusBar)])
{
UIStatusBarAnimation animation = UIStatusBarAnimationSlide;
if ([viewControllerConformingToAppearance respondsToSelector:@selector(prefferedStatusBarAnimation)])
{
animation = [viewControllerConformingToAppearance prefferedStatusBarAnimation];
}
[[UIApplication sharedApplication] setStatusBarHidden:(! [viewControllerConformingToAppearance showsStatusBar])
withAnimation:animation];
}
}
}
@end
Ahora, es hora de decir que su controlador de vista está implementando el AGViewControllerAppearance protocolo .
Ejemplo:
@interface XYSampleViewController () <AGViewControllerAppearance>
... the rest of the interface
@end
Por supuesto, se puede implementar el resto de los métodos ( showsStatusBar , animatesStatusBarVisibility , prefferedStatusBarAnimation ) del protocolo y UIViewController + Upgrade va a hacer la personalización adecuada en función de los valores proporcionados por ellos.
Tenga en cuenta que al usar el self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
solución
asegúrese de ir a su lista y establezca "Ver apariencia de la barra de estado basada en el controlador" en SÍ. Si es NO, no funcionará.
Desde Xcode 11.4, anulando el preferredStatusBarStyle
propiedad en una extensión UINavigationController ya no funciona ya que no se llamará.
Establecer el valor barStyle
de navigationBar
to .black
funciona, pero esto agregará efectos secundarios no deseados si agrega subvistas a la barra de navegación que pueden tener diferentes apariencias para el modo claro y oscuro. Porque al configurarlo barStyle
en negro, el userInterfaceStyle
de una vista que está incrustada en la barra de navegación siempre tendrá userInterfaceStyle.dark
independientemente userInterfaceStyle
de la aplicación.
La solución adecuada que se me ocurre es agregar una subclase de UINavigationController
y anular preferredStatusBarStyle
allí. Si luego utiliza este UINavigationController personalizado para todas sus vistas, estará en el lado de guardar.
NavigationController o TabBarController son los que necesitan proporcionar el estilo. Así es como lo resolví: https://stackoverflow.com/a/39072526/242769