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 UIViewControlleren el que había implementado preferredStatusBarStylefue 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 preferredStatusBarStylese 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 UIViewControllerBasedStatusBarAppearanceque NOen 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 UINavigationControllerno reenvía las preferredStatusBarStylellamadas 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 preferredStatusBarStyleen sus VC dentro de un controlador de navegación no hará nada, nunca se los llamará.
El truco es lo que la UINavigationControllerutiliza para decidir qué volver a UIStatusBarStyleDefaulto UIStatusBarStyleLightContent. Basa esto en su UINavigationBar.barStyle. El valor predeterminado ( UIBarStyleDefault) da como resultado la UIStatusBarStyleDefaultbarra de estado de primer plano oscuro . Y UIBarStyleBlackle dará una UIStatusBarStyleLightContentbarra de estado.
TL; DR:
Si quieres UIStatusBarStyleLightContentun UINavigationControlleruso:
self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
preferredStatusBarStyleen cuenta que , de hecho, se llamará al controlador de vista secundario si oculta la barra de navegación (establecida navigationBarHiddenen YES), exactamente según corresponda.
[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack]
navigationBarHiddenconjunto a YESrealmente habrá preferredStatusBarStylellamado, 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
supera 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 preferedStatusBarStylea 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 didFinishLaunchingWithOptionsmétodo.
Y no se olvide de establecer el UIViewControllerBasedStatusBarAppearanceque YESen 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, preferredStatusBarStyleno se llama porque topViewControllerse prefiere self. Entonces, para que te preferredStatusBarStylellamen 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 preferredStatusBarStylecontrolador 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.
preferredStatusBarStyley hace la lógica específica del UINavigationController. En este momento, esta lógica se basa, navigationBar.barStylepero puedo ver que se agregan verificaciones adicionales (por ejemplo, UISearchDisplayControllermover 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
}
}
UIViewControllerBasedStatusBarAppearanceclave info.plistya 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 NewFlowUIViewControllery luego agrega un nuevo controlador de navegación o tabBar a NewFlowUIViewController, luego agrega una extensión NewFlowUIViewControllertambién para administrar el estilo de la barra de estado del controlador de vista adicional.
En caso de que establezca modalPresentationStyle que no sea fullScreenmientras se presenta modalmente, debe establecerlo modalPresentationCapturesStatusBarAppearanceen verdadero para que el controlador de vista presentado deba recibir el control de apariencia de la barra de estado.
UINavigationControlleres 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 preferredStatusBarStyley 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 preferredStatusBarStyledentroUINavigationController
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 childForStatusBarStyledentroUINavigationController
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 UINavigationControllercó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
}
preferredStatusBarStyleSe 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 UIStatusBarStyleBlackTranslucenty UIStatusBarStyleBlackOpaqueestá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 UIViewControllerBasedStatusBarAppearanceclave al Info.plistarchivo 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 AppDelegateconjunto:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window!.rootViewController = myRootController
return true
}
myRootControllerpuede ser cualquier tipo de UIViewController, por ejemplo, UITabBarControllero 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 appearanceen SÍ en su Info.plistpara hacer que esto funcione (que es el valor predeterminado).
La mayoría de las respuestas no incluyen una buena implementación del childViewControllerForStatusBarStylemé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 preferredStatusBarStylemétodo en la UINavigationControllercategorí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 barStylede navigationBarto .blackfunciona, 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 barStyleen negro, el userInterfaceStylede una vista que está incrustada en la barra de navegación siempre tendrá userInterfaceStyle.darkindependientemente userInterfaceStylede la aplicación.
La solución adecuada que se me ocurre es agregar una subclase de UINavigationControllery anular preferredStatusBarStyleallí. 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