preferredStatusBarStyle no se llama

257

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).

trgoofi
fuente
No se llama en qué contexto: ¿simulador? en un dispositivo?
bneely
@bneely ambos.
trgoofi
¿Está utilizando el simulador de iOS 7, un dispositivo iOS 7 y su SDK base es 7.0?
bneely
@bneely iOS SDK 7.0 se muestra debajo del nombre de mi proyecto, ¿eso significa que mi SDK base es 7.0?
trgoofi
En la configuración de compilación, "Base SDK" es donde se establece el valor. Parece que su proyecto está configurado en 7.0.
bneely

Respuestas:

117

Posible causa raíz

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;
}

Método alternativo (en desuso en iOS 9)

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.

AbdullahC
fuente
2
Tengo el mismo problema que tú, no configuro el controlador de vista raíz. ¿Cómo demonios encontraste eso?
trgoofi
1
Sospeché que algo en el marco no recibía la notificación setNeedsStatusBarAppearanceUpdate; mis sospechas se confirmaron cuando hice este cambio.
AbdullahC
2
Un problema relacionado que encontré en una aplicación era un controlador de vista con un controlador de vista secundaria de pantalla completa que no anulaba childViewControllerForStatusBarStyle y childViewControllerForStatusBarHidden para devolver ese controlador de vista secundario. Si tiene su propia jerarquía de controlador de vista, debe proporcionar estos métodos para informar al sistema sobre qué controlador de vista debe usarse para determinar el estilo de la barra de estado.
Jon Steinmetz
configurar rootviewcontroller no cambia nada. Deberías trabajar con el comentario de Jon. Y tenga cuidado al llamar a setneedsstatusbarappearanceUpdate. Debe llamarlo desde el padre para trabajar.
doozMen
1
@Hippo, eres un genio !! ¿Cómo descubriste que era por no configurar rootviewcontroller?
ViruMax
1019

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;
Tyson
fuente
59
¡Agradable! Tenga 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.
Patrick Pijnappel
25
Gracias por esta respuesta Si desea establecer la barStyle para todas sus barras de navegación, llamada[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack]
Thomas Desierto
15
Respuesta perfecta. Ninguna de las otras respuestas sobre SO tomó en consideración el UINavigationController. 2 horas de golpearme la cabeza contra el teclado.
Ryan Alford
10
Felicitaciones a @Patrick por indicar que el navigationBarHiddenconjunto a YESrealmente habrá preferredStatusBarStylellamado, y una advertencia a aquellos que podrían tropezar con esto: ¡funciona con navigationBarHidden, pero no con navigationBar.hidden!
jcaron
44
debería ser obvio, pero también necesita "Ver la apariencia de la barra de estado basada en el controlador" establecido en SÍ en Info.plist para que esto funcione.
Code Baller
99

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?)

serenn
fuente
Parece que return self.topViewController;funciona para mí, pero return self.visibleViewController;, no
k06a
visibleViewController puede devolver el controlador modal actualmente presentado cuando lo descarta. Lo cual es un fastidio. Use topViewController.
Ben Sinclair
1
@ d.lebedev ok, pero no creo que ninguno de esos problemas se aplique aquí. No necesita llamar supera este método y realmente desea cambiar el comportamiento de todos los controladores de este tipo
ed '
1
Esto no me funciona en iOS 9.3. Supongo que este es el problema: este problema es de particular importancia porque muchas de las clases de Cocoa se implementan utilizando categorías. Un método definido por el marco que intenta anular puede haberse implementado en una categoría, por lo que la implementación tiene prioridad no está definida.
vikingosegundo
2
Esto está mal y se rompe en iOS 13.4. Porque extender las clases del objetivo C en Swift se implementa a través de las categorías del objetivo C. No se recomienda anular 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
79

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
    }
}
Alex Brown
fuente
10
Usted señor se merece una medalla.
nikans
2
Muchas gracias hombre. En cambio, estaba volviendo visibleViewController sin éxito.
Fábio Salata
1
Esto es oro Tengo un controlador de navegación incrustado en una barra de pestañas y acabo de lanzar esto en un archivo y ahora puedo cambiar la apariencia de la barra de estado en cualquier lugar que desee.
Vahid Amiri
2
Esto está mal y se rompe en iOS 13.4. Porque extender las clases del objetivo C en Swift se implementa a través de las categorías del objetivo C. No se recomienda anular 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
1
@MarcEtcheverry esta instancia en particular no estuvo mal. El hecho es que las subclases de otros objetos / protocolos como UINavigationController no tenían una implementación previa de estos para entrar en conflicto en el despacho dinámico. No hubo valores predeterminados o implementaciones dentro de las subclases reales, por lo que esta fue la forma más limpia de implementar esto en una aplicación sin crear una dependencia innecesaria (punto). Desafortunadamente, 13.4 parece haber cambiado este comportamiento. Supongo que detrás de escena tienen una verificación o implementación ahora que no ha existido durante años .........
TheCodingArt
20

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
    }
}
Luis
fuente
1
Esta es la única solución que funciona. Todas las respuestas en SO apuntan a la solución estándar que no funcionará para ninguna aplicación con NavigationControllers. ¡¡¡Gracias!!!
Houman
Usar extensiones para anular es simplemente incorrecto. Eso no es seguro. Existen múltiples soluciones más simples. Use una subclase en su lugar.
Sulthan
2
Esto está mal y se rompe en iOS 13.4. Porque extender las clases del objetivo C en Swift se implementa a través de las categorías del objetivo C. No se recomienda anular 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
1
@MarcEtcheverry esta instancia en particular no estuvo mal. El hecho es que las subclases de otros objetos / protocolos como UINavigationController no tenían una implementación previa de estos para entrar en conflicto en el despacho dinámico. No hubo valores predeterminados o implementaciones dentro de las subclases reales, por lo que esta fue la forma más limpia de implementar esto en una aplicación sin crear una dependencia innecesaria (punto). Desafortunadamente, 13.4 parece haber cambiado este comportamiento. Supongo que detrás de escena tienen una verificación o implementación ahora que no ha existido durante años .........
TheCodingArt
15

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á.

Código

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
     // status bar appearance code
     [[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];

     return YES;
}
Yogesh Suthar
fuente
14

En un UINavigationController, preferredStatusBarStyleno se llama porque topViewControllerse prefiere self. Entonces, para que te preferredStatusBarStylellamen en un UINavigationController, debes cambiarlo childViewControllerForStatusBarStyle.

Recomendación

Anule su UINavigationController en su clase:

class MyRootNavigationController: UINavigationController {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
    override var childViewControllerForStatusBarStyle: UIViewController? {
        return nil
    }
}

Alternativa no recomendada

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
    }
}
Coeur
fuente
11

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.

frin
fuente
9

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.

Artem Abramov
fuente
No hagas esto, funciona por ahora, pero puede romper el comportamiento futuro. Simplemente cambie el estilo navBar - vea mi respuesta stackoverflow.com/a/19513714/505457
Tyson
2
Debe usar la subclase, no la categoría.
shuiyouren
2Tyson: ¿Por qué romperá el comportamiento futuro? preferredStatusBarStyle: es el método preferido de Apple para configurar el estilo de la barra de estado.
Artem Abramov
2shuiyouren: ¿Por qué debería aumentar la complejidad subclasificando si solo puedo usar una categoría e incluirla en cada lugar donde lo haga? De todos modos, esa es una cuestión de arquitectura, no de implementación.
Artem Abramov
2
@ArtemAbramov Porque el UINavigationController ya implementa 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.
Tyson
9

Swift 4.2 y superior

Como se menciona en la respuesta seleccionada , la causa raíz es verificar el objeto del controlador de vista raíz de la ventana.

Posibles casos de su estructura de flujo

  • El objeto UIViewController personalizado

    es un controlador de vista de raíz de ventana.

    Este tipo de flujo generalmente se usa si su aplicación tiene un flujo de inicio de sesión previo en la pila de navegación sin pestañas y un flujo de inicio de sesión posterior con pestañas y posiblemente cada pestaña contenga aún más el controlador de navegación.

  • El objeto TabBarController es el controlador de vista raíz de la ventana.

    Este es el flujo en el que el controlador de vista raíz de la ventana es tabBarController, posiblemente todas las pestañas tienen más control de navegación.

  • El objeto NavigationController es el controlador de vista raíz de ventana

    Este es el flujo donde el controlador de vista raíz de ventana es navigationController.

    No estoy seguro de si existe la posibilidad de agregar un controlador de barra de pestañas o un nuevo controlador de navegación en un controlador de navegación existente. Pero si es así, debemos pasar el control de estilo de la barra de estado al siguiente contenedor. Entonces, agregué la misma verificación en la extensión UINavigationController para encontrarchildForStatusBarStyle

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
    }
}
  • No necesita la UIViewControllerBasedStatusBarAppearanceclave info.plistya que es verdadera por defecto

Puntos a considerar para flujos más complejos

  • En 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.

abhimanyu jindal
fuente
Excelente respuesta!
Amin Benarieb
3
Esto está mal y se rompe en iOS 13.4. Porque extender las clases del objetivo C en Swift se implementa a través de las categorías del objetivo C. No se recomienda anular 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 esta instancia en particular no estuvo mal. El hecho es que las subclases de otros objetos / protocolos como UINavigationController no tenían una implementación previa de estos para entrar en conflicto en el despacho dinámico. No hubo valores predeterminados o implementaciones dentro de las subclases reales, por lo que esta fue la forma más limpia de implementar esto en una aplicación sin crear una dependencia innecesaria (punto). Desafortunadamente, 13.4 parece haber cambiado este comportamiento. Supongo que detrás de escena tienen una verificación o implementación ahora que no ha existido durante años .........
TheCodingArt
8

Soluciones iOS 13

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:

  1. Optar por salir del modo oscuro por completo
    • En su info.plist, agregue la siguiente propiedad:
      • Clave - UIUserInterfaceStyle(también conocido como "Estilo de interfaz de usuario")
      • Valor: luz
  2. Anular preferredStatusBarStyledentroUINavigationController

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

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

      O

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

    • childForStatusBarStyle( doc ): se llama cuando el sistema necesita el controlador de vista para determinar el estilo de la barra de estado
    • De acuerdo con 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 sí mismo . 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 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ón
      • visibleViewController(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 😎

Andrew Kirna
fuente
Mi barra de estado se pone negra después de la rotación de la pantalla. ¿Alguna idea de por qué? Esto solo sucede en el simulador de iPad Pro.
Pedro Paulo Amorim
@PedroPauloAmorim, ¿puedes proporcionar más información? ¿Cómo se presenta el controlador de vista superior (modal, pantalla completa, espectáculo)? ¿Está anidado dentro de un controlador de navegación? ¿El texto se vuelve negro o el fondo también? ¿Qué estás tratando de lograr?
Andrew Kirna
Configuré la barra de estado de la luz en toda mi aplicación. Obtiene luz en dos rotaciones, en la tercera se oscurece y nunca vuelve a la luz, incluso obligando a volver a dibujarla. Está sucediendo en el simulador de iPad Pro. Las vistas se presentan en pantalla completa y no están anidadas dentro de un controlador de navegación. Solo el texto se oscurece.
Pedro Paulo Amorim
¿Cómo está configurando la barra de estado de la luz en primer lugar?
Andrew Kirna
3
Su anulación a través de la extensión no es una anulación real. Es un mal uso inseguro del lenguaje. Eso puede romperse muy fácilmente.
Sulthan
7

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
}
John Stricker
fuente
2
Esto está mal y se rompe en iOS 13.4. Porque extender las clases del objetivo C en Swift se implementa a través de las categorías del objetivo C. No se recomienda anular 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 esta instancia en particular no estuvo mal. El hecho es que las subclases de otros objetos / protocolos como UINavigationController no tenían una implementación previa de estos para entrar en conflicto en el despacho dinámico. No hubo valores predeterminados o implementaciones dentro de las subclases reales, por lo que esta fue la forma más limpia de implementar esto en una aplicación sin crear una dependencia innecesaria (punto). Desafortunadamente, 13.4 parece haber cambiado este comportamiento. Supongo que detrás de escena tienen una verificación o implementación ahora que no ha existido durante años .........
TheCodingArt
6

Si su viewController está bajo UINavigationController.

Subclase UINavigationController y agregar

override var preferredStatusBarStyle: UIStatusBarStyle {
    return topViewController?.preferredStatusBarStyle ?? .default
}

preferredStatusBarStyleSe llamará a ViewController .

PowHu
fuente
1
vea esta publicación: medium.com/@_riteshhh/swift-snippet-1-c2d485028185#.yg96zi7ah
Harry Bloom el
4

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.

oscarr
fuente
3

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
    }

}
Benjamin Lowry
fuente
1
¿Pero qué pasa si mi barra de navegación está oculta?
Slavcho
1
Porque necesito que la navegación esté oculta y que la barra de estado esté visible.
Slavcho
1

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).

Damnum
fuente
@ ¿Cómo funciona en swift3?
Avión
1

Solución Swift 3 para iOS 10:

override var preferredStatusBarStyle: UIStatusBarStyle {
    return .lightContent
 }
Statik
fuente
1

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
}
Timur Bernikovich
fuente
1

En mi caso, he presentado accidentalmente el controlador de vista / navegación como UIModalPresentationStyle.overFullScreen, lo que hace que preferredStatusBarStyleno se llame. Después de volver a cambiarlo UIModalPresentationStyle.fullScreen, todo funciona.

Casey
fuente
1

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];
Kevin Flachsmann
fuente
0

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.

arturgrigor
fuente
0

Si alguien se encuentra con este problema con UISearchController. Simplemente cree una nueva subclase de UISearchController y luego agregue el código a continuación en esa clase:

override func preferredStatusBarStyle() -> UIStatusBarStyle {
    return .LightContent
}
Tai Le
fuente
0

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á.

Richard Garfield
fuente
Establecer UIViewControllerBasedStatusBarAppearance en YES en la lista de proyectos marcó la diferencia para mí. Me había olvidado de eso.
filo
0

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.

PatrickDotStar
fuente
-1

NavigationController o TabBarController son los que necesitan proporcionar el estilo. Así es como lo resolví: https://stackoverflow.com/a/39072526/242769

aryaxt
fuente
Si cree que se trata de un duplicado de otra pregunta, cierre la votación como duplicado