UIStatusBarStyle PreferredStatusBarStyle no funciona en iOS 7

110

En mi aplicación para iPhone construido con Xcode 5 para el sistema iOS 7 I UIViewControllerBasedStatusBarAppearance=YESen info.plist, y en mi ViewControllerTengo este código:

-(UIStatusBarStyle) preferredStatusBarStyle
{
    return UIStatusBarStyleLightContent;
}

Pero la barra de estado sigue siendo negra sobre fondo negro.

Sé que es posible cambiar esta aplicación en todo mediante el establecimiento UIViewControllerBasedStatusBarAppearance=NOde info.plist, pero en realidad lo que necesito para alterar esto en un viewControllerpor viewControllerbase en tiempo de ejecución.

Andrew Smith
fuente
Hola, tengo el mismo problema que mencionaste en cuestión. ¿Obtuviste la solución? Por favor, dame eso.
Noundla Sandeep

Respuestas:

281

Descubrí que si su ViewController está dentro de un navigationController, el navigationController navigationBar.barStyledetermina el statusBarStyle.

Configurar su barra de navegación barStyleen UIBarStyleBlackTranslucentdará un texto de barra de estado blanco (es decir UIStatusBarStyleLightContent), y UIBarStyleDefaultdará un texto de barra de estado negro (es decir UIStatusBarStyleDefault).

Tenga en cuenta que esto se aplica incluso si cambia totalmente el color de la barra de navegación a través de su barTintColor.

mxcl
fuente
esto tiene sentido para mí ... genial
Nick
14
Creo que es porque el UINavigationController's preferredStatusBarStyleno llama al ViewController que aloja, y en su lugar solo regresa basado en su navigationBarStyle.
mxcl
En este caso, la vista no está dentro de un controlador de navegación.
Andrew Smith
Es muy contradictorio pensar que el estilo de barra tiene preferencia sobre un método implementado en el controlador de vista, ¡y solo cuando se presentan vistas modales!
Matej
3
UIBarStyleBlackTranslucent está obsoleto, utilice UIBarStyleBlacken su lugar
Nhon Nguyen
87

Bien, aquí está el truco. Debe agregar la tecla "Ver barra de estado basada en el controlador" y establecer el valor en No.

Esto va en contra de lo que parece ser el significado de esta clave, pero incluso si establece el valor en No, aún puede cambiar la apariencia de la barra de estado y si se muestra o no en cualquier controlador de vista. Así que actúa como "Sí", ¡pero configúrelo en "No"!

Ahora puedo poner la barra de estado en blanco u oscuro.

Andrew Smith
fuente
6
Para mí esto estaba mal. La clave debe establecerse en "Sí", como era de esperar. Estoy en Xcode 5.1 iOS 7.1, así que tal vez haya cambiado.
joel.d
Estoy usando Xcode 5.1 y iOS 7.1 también y NO funcionó para mí ... EXTRAÑO.
Arjun Mehta
¿Dónde debo agregar esta clave?
Hadu
En su archivo [AppName] -Info.plist
Saren Inden
1
Funciona bien cuando la tecla "Ver la barra de estado basada en el controlador" se establece en "SÍ" con Xcode6.0, iOS 8.0
bpolat
77

Para preferredStatusBarStyle()trabajar dentro UINavigationControllery UITabBarControlleragrego el siguiente código, que obtendrá el estilo de barra de estado preferido del controlador de vista actualmente visible.

extension UITabBarController {
    public override func childViewControllerForStatusBarStyle() -> UIViewController? {
        return selectedViewController
    }
}

extension UINavigationController {
    public override func childViewControllerForStatusBarStyle() -> UIViewController? {
        return visibleViewController
    }
}

Para Swift 3 esos no son métodos sino propiedades:

extension UITabBarController {
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return selectedViewController
    }
}

extension UINavigationController {
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return visibleViewController
    }
}

Las propiedades de Swift 4.2 han cambiado de nombre:

extension UITabBarController {
   open override var childForStatusBarStyle: UIViewController? {
        return selectedViewController
    }
}

extension UINavigationController {
   open override var childForStatusBarStyle: UIViewController? {
        return visibleViewController
    }
}

Uso

class ViewController: UIViewController {

    // This will be called every time the ViewController appears
    // Works great for pushing & popping
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }

}
Daniel Wood
fuente
6
Esta es, con mucho, la mejor respuesta (para aplicaciones que usan UINavigationController o UITabBarController
Kesava
1
¿cuál es el uso de esto?
AnBisw
@Annjawn UIKit utiliza estos métodos. No necesita hacer nada más que agregarlo a su proyecto.
Daniel Wood
@DanielWood sí, me di cuenta de eso y olvidé por completo que usé exactamente lo mismo en uno de mis proyectos anteriores, aunque de manera ligeramente diferente.
AnBisw
De hecho, esta es la mejor respuesta
Musa almatri
33

Puede que llegue a esto un poco tarde, pero en caso de que alguien más esté buscando una solución completa de aplicación verificada y que funcione.

@mxcl tiene razón al describir por qué sucede esto. Para corregirlo, simplemente creamos una extensión (o categoría en obj-c) que anula el método preferidoSatusBarStyle () de UINavigationController. Aquí hay un ejemplo en Swift:

extension UINavigationController {
    public override func preferredStatusBarStyle() -> UIStatusBarStyle {
        if let rootViewController = self.viewControllers.first {
            return rootViewController.preferredStatusBarStyle()
        }
        return super.preferredStatusBarStyle()
    }
}

Este código simplemente extrae el primer controlador de vista (el controlador de vista raíz) y lo desenvuelve (en obj-c simplemente verifique que no sea nulo). Si el desenvolvimiento es exitoso (no nulo), entonces tomamos rootViewControllers favoriteStatusBarStyle. De lo contrario, devolvemos el valor predeterminado.

Espero que esto ayude a cualquiera que lo necesite.

Kyle Begeman
fuente
2
En Swift 2.0 debe eliminar "como? UIViewController" de la declaración de condición.
Thomás Calmon
Genial, hice una modificación además de eliminar la declaración "como", la cambié de "primera" a "última" de esta manera, cualquier controlador de vista que esté viendo el usuario en la parte superior de la pila tendrá la capacidad de controlar el color de la barra de estado. ¡Excelente trabajo, gracias por compartir!
Unome
1
Si su controlador de navegación no tiene ningún controlador de vista, esto provocaría un bucle infinito. return self.preferredStatusBarStyle()volvería a llamar en este mismo método exacto.
bearMountain
1
En mi caso, en lugar de usar rootViewController, usé topViewController ya que durante la pila el estilo puede cambiar.
Ric Santos
2
@Unome visibleViewControllersería aún mejor
Cœur
21

Para proporcionar más detalles sobre la respuesta aceptada, coloque la siguiente línea en el didFinishLaunchingWithOptions:método del delegado de su aplicación :

[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;

Luego, en su Info.plist, agréguelo View controller-based status bar appearancey configúrelo en NO.

Creo que así es como debe hacerse, NO desde el controlador de navegación, si desea el mismo color de barra de estado para toda la aplicación. Es posible que tenga pantallas que no estén necesariamente incrustadas en una subclase UINavigationControllerdiferente UINavigationControlleren otro lugar o en otras cosas.

EDITAR : También puede hacerlo sin escribir ningún código: https://stackoverflow.com/a/18732865/855680

Mateo Quiros
fuente
1
Tenga en cuenta que esta forma está obsoleta desde IOS 9.0
Mohamed Salah
10

En viewDidLoad solo escribe esto

[self setNeedsStatusBarAppearanceUpdate];

solo haz eso y funcionará

¿Puedes intentar esto?

Set UIViewControllerBasedStatusBarAppearance to NO.
Call [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

Una cosa más que he visto en su pregunta es que ha escrito el método de esta manera.

 -(void)UIStatusBarStyle PreferredStatusBarStyle ()
        {
            return UIStatusBarStyle.LightContent;
        }

pero debería ser así

-(UIStatusBarStyle)preferredStatusBarStyle{ 
    return UIStatusBarStyleLightContent; 
} 
Usuario 1531343
fuente
Esto hace que se llame al método preferidoStatusBarStyle, pero aún así la barra de estado es negra.
Andrew Smith
por favor vea mi respuesta actualizada ... avíseme rápidamente si eso funciona o no
Usuario 1531343
Mi pregunta original dice explícitamente que necesito ver por control de vista de la barra de estado.
Andrew Smith
¿Puedes comprobar tu código con referencia a mi pregunta actualizada?
Usuario 1531343
1
[self setNeedsStatusBarAppearanceUpdate];un gran método, ¡gracias!
Supertecnoboff
6

Así es como lo resolví. Por lo general, el navigationController o tabBarController son los que deciden la apariencia de la barra de estado (oculta, color, etc.).

Así que terminé subclasificando el controlador de navegación y anulando favoriteStatusBarStyle. si el ViewContorller visible actual implementa StatusBarStyleHandler, pido que el valor se use como estilo, si no es así, solo devuelvo un valor predeterminado.

La forma en setNeedsStatusBarAppearanceUpdateque activa una actualización de la apariencia de la barra de estado es llamando a cuál se activa de preferredStatusBarStylenuevo y actualiza la interfaz de usuario de acuerdo con lo que devuelve el método.

public protocol StatusBarStyleHandler {
    var preferredStatusBarStyle: UIStatusBarStyle { get }
}

public class CustomNavigationCotnroller: UINavigationController {

    public override var preferredStatusBarStyle: UIStatusBarStyle {
        if let statusBarHandler = visibleViewController as? StatusBarStyleHandler {
            return statusBarHandler.preferredStatusBarStyle
        }

        return .default
    }
}

Entonces uso

public class SomeController: UIViewController, StatusBarStyleHandler {

    private var statusBarToggle = true

    // just a sample for toggling the status bar style each time method is called
    private func toggleStatusBarColor() {
        statusBarToggle = !statusBarToggle
        setNeedsStatusBarAppearanceUpdate()
    }

    public override var preferredStatusBarStyle: UIStatusBarStyle {
        return statusBarToggle ? .lightContent : .default
    }
}
aryaxt
fuente
Esta publicación mejoraría mucho si pudiera explicar por qué y cómo soluciona el problema.
En lugar de subclasificar UINavigationController, también puede crear una extensión para UINavigationController y lograr el mismo resultado sin tener que subclasificar.
wilforeal
4

1) Una configuración para todo el proyecto:

Si está disponible, elimine el UIViewControllerBasedStatusBarAppearancepar clave-valor de su info.plist o configúrelo NOsin eliminarlo. Si no está disponible en su info.plist, no haga nada. El valor predeterminado es NOpara esta propiedad.

Agregue el siguiente código a su AppDelegate.m:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
}

2) Diferentes configuraciones para diferentes controladores de vista:

Agregue UIViewControllerBasedStatusBarAppearanceun par clave-valor a su info.plist y configúrelo en YES.

Si su View Controller no está integrado en Navigation Controller. Digamos MyViewController. simplemente agregue el código a continuación a su archivo MyViewController.m. Si su View Controller está integrado en Navigation Controller, cree una nueva Clase Cocoa Touch y conviértala en una subclase de UINavigationController. Digamos MyNC. Seleccione Vista del controlador de navegación en su Storyboard, en el panel derecho; Utilidades -> Inspector de identidad -> Clase personalizada -> Clase, escriba "MyNC". Después de vincular Storyboard View con su Clase Cocoa Touch "MyNC", agregue el código a continuación a su MyNC.m:

- (BOOL)prefersStatusBarHidden {
    return NO;
}

-(UIStatusBarStyle)preferredStatusBarStyle {
    return UIStatusBarStyleLightContent;
}
Fatih Aksu
fuente
Parece que en iOS9 UIViewControllerBasedStatusBarAppearance contiene el valor YES de forma predeterminada, ya que necesitaba agregarlo manualmente en .plist y establecerlo en NO para que funcione correctamente.
Mohd Asim
4

Incluso con todas las respuestas aquí, todavía no encontré la solución exacta para mí, pero comencé con la respuesta de Daniel. Con lo que terminé fue:

override var preferredStatusBarStyle: UIStatusBarStyle {
     return visibleViewController?.preferredStatusBarStyle ?? .lightContent
}

en los controladores de navegación (similar para la pestaña, solo selectedViewController). Y luego respetará:

override var preferredStatusBarStyle: UIStatusBarStyle {
     return .lightContent
}

En cada controlador de vista, a menos que lo establezca de otra manera. No necesito llamar a setNeedsStatusBarAppearanceUpdate()ningún lado, solo se actualiza cuando llegas a cada controlador de vista.

Andrew Plummer
fuente
2
Terminé con la solución casi idéntica después de luchar con esto durante horas.
Scott Jungwirth
En algún momento, esto parece haber sido solucionado, solo usar favoriteStatusBarStyle en cada VC funciona bien para mí ahora.
Andrew Plummer
4

Soluciones iOS 13

La respuesta más votada usa código "heredado" 👎

La configuración de la barStylepropiedad ahora (iOS 13+) se considera una "personalización heredada". Según Apple ,

En iOS 13 y versiones posteriores, personalice la barra de navegación con las propiedades standardAppearance, compactAppearance y scrollEdgeAppearance. Puede seguir utilizando estos accesos heredados para personalizar la apariencia de la barra de navegación directamente, pero debe actualizar la apariencia para las diferentes configuraciones de barra.

En cuanto a su intento, ¡estaba en el camino correcto!

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 subclase de UIViewController, hereda preferredStatusBarStyley childForStatusBarStyle, que puede establecer como desee.

Cualquiera de los siguientes métodos debería funcionar:

  1. Anular preferredStatusBarStyledentroUINavigationController

    • preferredStatusBarStyle( doc ): el estilo de barra de estado preferido para el controlador de vista
    • Subclase o extender UINavigationController

      class MyNavigationController: UINavigationController {
          override var preferredStatusBarStyle: UIStatusBarStyle {
              .lightContent
          }
      }

      O

      extension UINavigationController {
          open override var preferredStatusBarStyle: UIStatusBarStyle {
              .lightContent
          }
      }
  2. Anular childForStatusBarStyledentroUINavigationController

    • childForStatusBarStyle( doc ): se llama cuando el sistema necesita que el controlador de vista se use para determinar el estilo de la barra de estado
    • Según la documentación de Apple,

      "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 self . Si el valor de retorno de este método cambia, llame al método setNeedsStatusBarAppearanceUpdate (). "

    • En otras palabras, si no implementa la solución 3 aquí, el sistema recurrirá a la solución 2 anterior.
    • Subclase o extender 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ón
      • visibleViewController(of UINavigationController) ( doc ): el controlador de vista asociado con la vista actualmente visible en la interfaz de navegación (pista: esto puede incluir "un controlador de vista que se presentó de forma modal en la parte superior del controlador de navegación")

Nota: Si decide crear una 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 😎

Andrew Kirna
fuente
1
Respuesta muy completa, gracias! Además, algo con lo que luché durante un tiempo, en iOS 13, el .defaultestilo tiene en cuenta el modo oscuro y no está documentado, por lo que si también es compatible con versiones anteriores de iOS, puede agregarlo if #available(iOS 13, *) { return .darkContent } else { return .default }si intenta configurar el estilo de la barra de estado manualmente de acuerdo con el color detrás de la barra de estado y ese color es "brillante".
Valcanaia
1
Tenga en cuenta que el método de extensión para anular var ya no funciona en Xcode 11.4 / iOS 13.4
Marc Etcheverry
Porque la extensión de las clases objetivo C en Swift se implementa a través de las categorías Objective C. No se recomienda reemplazar los métodos a través de las categorías del Objetivo C y es probable que se rompan. Ver stackoverflow.com/a/38274660/2438634
Marc Etcheverry
Aunque anular el UINavigationController definitivamente funciona, parece un error del lado de Apple que no hace el childForStatusBarStyle por defecto devolviendo su topViewController. Por ejemplo, UITabBarController hace esto para sus pestañas. Para mí, no hay ninguna razón por la que UINavigationController, siendo estrictamente un controlador de contenedor para alojar controladores de vista "reales" en lugar de presentar su propia interfaz de usuario, deba "comerse" esos estilos de barra de estado. Creará un radar para Apple.
Igor Vasilev
1

Si en caso de que quisiera ocultar la barra de estado durante la pantalla de presentación, pero quisiera cambiar el estilo a contenido ligero (StatusBarInitiallyHidden en Plist tiene que ser NO para ocultar barra de estado en la presentación), puede agregar esto al método didFinishLaunchingWithOptions de appDelegate para cambiar a lightContent.

[[UIApplication sharedApplication]setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
[[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleLightContent];
aalesano
fuente
1

ejemplo rápido

en AppDelegate.swift

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
    UIApplication.sharedApplication().statusBarStyle = UIStatusBarStyle.LightContent;

    return true
}

en info.plist set Ver la apariencia de la barra de estado basada en el controlador: NO

Fyalavuz
fuente
1

Si está usando NavigationController, puede crear una subclase NavigationControllerpara que consulte su controlador de vista secundario

// MyCustomNavigationController

- (NSUInteger)supportedInterfaceOrientations {
    UIViewController *viewControllerToAsk = [self findChildVC];
    return [viewControllerToAsk supportedInterfaceOrientations];
}

- (BOOL)shouldAutorotate {
    UIViewController *viewControllerToAsk = [self findChildVC];
    return [viewControllerToAsk shouldAutorotate];
}

- (UIStatusBarStyle)preferredStatusBarStyle {
    UIViewController *viewControllerToAsk = [self findChildVC];
    return [viewControllerToAsk preferredStatusBarStyle];
}

- (UIViewController *)findChildVC {
    return self.viewControllers.firstObject;
}
onmyway133
fuente
1

Rápido 4.2

extension UITabBarController {
    open override var childForStatusBarStyle: UIViewController? {
        return selectedViewController
    }
}

extension UINavigationController {
    open override var childForStatusBarStyle: UIViewController? {
        return visibleViewController
    }
}
Vyacheslav
fuente
Tenga en cuenta que el método de extensión para hacer override var ya no funciona en Xcode 11.4 / iOS 13.4
Marc Etcheverry
@MarcEtcheverry entonces, ¿por qué rechazaste la respuesta? parece extraño.
Vyacheslav
Porque la extensión de las clases objetivo C en Swift se implementa a través de las categorías Objective C. No se recomienda reemplazar los métodos a través de las categorías del Objetivo C y es probable que se rompan. Ver stackoverflow.com/a/38274660/2438634
Marc Etcheverry
@MarcEtcheverry 'not recommended'! = '¡Nunca lo use!'. para julio de 2018 la respuesta fue correcta. Incluso esta respuesta no está actualizada, esta no es una razón para rechazarla. No puedo ver el futuro
Vyacheslav
0

Puede configurar el estilo de la barra de estado. Se parecerá a la barra de estado como IOS 6 e inferior.
Pegue estos métodos en su controlador de vista

-(UIStatusBarStyle)preferredStatusBarStyle{
    return UIStatusBarStyleBlackOpaque;
}

y llamar a este método desde la vista se cargó así

if([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f)
    {
       [self setNeedsStatusBarAppearanceUpdate];
    }
Ganapatía
fuente
¿Te refieres [self setStatusBarNeedsUpdate]al segundo bloque? (O algo más al menos).
mxcl
0

Solo quiero agregar una nota para un caso específico que enfrenté. Tenía otra UIWindow en mi aplicación para mostrar una cara de chat que flotaba por toda mi aplicación todo el tiempo. Hacer esto hizo que ninguna de las soluciones anteriores funcionara, ¡y no estoy realmente seguro de por qué! ¡Todo lo que he notado es que mi ViewController en la nueva UIWindow fue la razón de eso! Y si quisiera cambiar el estilo de la barra de estado, tengo que hacerlo en ese controlador de vista de la nueva UIWindow.

¡Esta nota podría ayudar a otros que tienen una estructura similar! Entonces, básicamente, puede aplicar las soluciones mencionadas anteriormente en el ViewController de la nueva UIWindow.

Nuevamente este es un caso específico.

Gracias

Ehab Saifan
fuente
-1

Para swift 3, en su UIViewController:

override var preferredStatusBarStyle : UIStatusBarStyle { return UIStatusBarStyle.lightContent }
Mavrick Laakso
fuente