UIStatusBarStyle PreferredStatusBarStyle ne fonctionne pas sur iOS 7

110

Dans mon application iPhone construit avec Xcode 5 pour voir iOS 7 I UIViewControllerBasedStatusBarAppearance=YESdans info.plistet dans mon ViewControllerje ce code:

-(UIStatusBarStyle) preferredStatusBarStyle
{
    return UIStatusBarStyleLightContent;
}

Mais la barre d'état est toujours noire sur fond noir.

Je sais que possible de changer cette application à l' échelle en mettant UIViewControllerBasedStatusBarAppearance=NOen info.plist, mais je dois en fait de modifier ce sur viewControlleren viewControllerbase à l' exécution.

Andrew Smith
la source
Salut, j'ai le même problème que vous avez mentionné en question. Avez-vous la solution? Veuillez me fournir cela.
Noundla Sandeep

Réponses:

281

J'ai découvert que si votre ViewController se trouve à l'intérieur d'un navigationController, le navigationController navigationBar.barStyledétermine le statusBarStyle.

Régler votre barre de navigation barStylesur UIBarStyleBlackTranslucentdonnera un texte de barre d'état blanc (c'est-à-dire UIStatusBarStyleLightContent), et UIBarStyleDefaultdonnera un texte de barre d'état noir (c'est-à-dire UIStatusBarStyleDefault).

Notez que cela s'applique même si vous changez totalement la couleur de navigationBar via son barTintColor.

mxcl
la source
cela a du sens pour moi ... super
Nick
14
Je crois que c'est parce que le UINavigationController's preferredStatusBarStylen'appelle pas par le biais du ViewController qu'il héberge, et retourne simplement en fonction de son navigationBarStyle.
mxcl
Dans ce cas, la vue n'est pas à l'intérieur d'un contrôleur de navigation.
Andrew Smith
Il est très contre-intuitif de penser que le style de barre a priorité sur une méthode implémentée dans le contrôleur de vue, et uniquement lors de la présentation de vues modales!
Matej
3
UIBarStyleBlackTranslucent est obsolète, utilisez à la UIBarStyleBlackplace
Nhon Nguyen
87

OK, voici le truc. Vous devez ajouter la clé "Afficher la barre d'état basée sur le contrôleur" et définir la valeur sur Non.

Cela va à l'encontre de ce que semble être la signification de cette clé, mais même si vous définissez la valeur sur No, vous pouvez toujours modifier l'apparence de la barre d'état et savoir si elle s'affiche ou non dans n'importe quel contrôleur de vue. Il agit donc comme "Oui" mais réglez-le sur "Non"!

Maintenant, je peux obtenir la barre d'état blanche ou sombre.

Andrew Smith
la source
6
Pour moi, c'était faux. La clé devait être définie sur "Oui", comme vous vous en doutez. Je suis sur Xcode 5.1 iOS 7.1, alors peut-être que cela a changé.
joel.d
J'utilise également Xcode 5.1 et iOS 7.1 et NON a fonctionné pour moi ... ÉTRANGE.
Arjun Mehta
Où dois-je ajouter cette clé?
Hadu
Dans votre fichier [AppName] -Info.plist
Saren Inden
1
Cela fonctionne bien lorsque la touche "Afficher la barre d'état basée sur le contrôleur" est définie sur "OUI" avec Xcode6.0, iOS 8.0
bpolat
77

Pour preferredStatusBarStyle()travailler dedans UINavigationControlleret UITabBarControllerj'ajoute le code suivant, qui obtiendra le style de barre d'état préféré du contrôleur de vue actuellement visible.

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

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

Pour Swift 3, ce ne sont pas des méthodes mais des propriétés:

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

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

Les propriétés Swift 4.2 ont été renommées:

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

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

Usage

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
la source
6
C'est de loin la meilleure réponse (pour les applications qui utilisent UINavigationController ou UITabBarController
Kesava
1
à quoi ça sert?
AnBisw
@Annjawn ces méthodes sont utilisées par UIKit. Vous n'avez rien d'autre à faire que de l'ajouter à votre projet.
Daniel Wood
@DanielWood ouais j'ai compris cela et j'ai complètement oublié que j'avais utilisé exactement la même chose dans l'un de mes projets précédents, bien que légèrement différemment.
AnBisw
C'est en effet la meilleure réponse
Musa almatri
33

J'arrive peut-être un peu tard, mais si quelqu'un d'autre cherche une solution fonctionnelle et vérifiée à l'échelle de l'application.

@mxcl a raison de décrire pourquoi cela se produit. Pour le corriger, nous créons simplement une extension (ou une catégorie dans obj-c) qui remplace la méthode favoriteSatusBarStyle () de UINavigationController. Voici un exemple dans Swift:

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

Ce code extrait simplement le premier contrôleur de vue (le contrôleur de vue racine) et le déballe (dans obj-c, vérifiez simplement qu'il n'est pas nul). Si le dépliage est réussi (pas nul), nous récupérons le rootViewControllers preferestatusBarStyle. Sinon, nous retournons simplement la valeur par défaut.

J'espère que cela aidera tous ceux qui pourraient en avoir besoin.

Kyle Begeman
la source
2
Dans Swift 2.0, vous devez supprimer "as? UIViewController" de l'instruction de condition.
Thomás Calmon
Génial, j'ai fait une modification en plus de supprimer l'instruction "as", je l'ai changée de "premier" à "dernier" de cette façon, quel que soit le contrôleur de vue vu par l'utilisateur en haut de la pile, il aura la possibilité de contrôler la couleur de la barre d'état. Excellent travail, merci pour le partage!
Unome
1
Si votre contrôleur de navigation ne possède aucun contrôleur de vue, cela entraînerait une boucle infinie. return self.preferredStatusBarStyle()rappellerait exactement la même méthode.
bearMountain le
1
Dans mon cas, au lieu d'utiliser le rootViewController, j'ai utilisé le topViewController car pendant la pile, le style peut changer.
Ric Santos
2
@Unome visibleViewControllerserait encore mieux
Cœur
21

Pour fournir plus de détails sur la réponse acceptée, placez la ligne suivante dans la didFinishLaunchingWithOptions:méthode de votre délégué d'application :

[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;

Ensuite, dans votre Info.plist, ajoutez-le View controller-based status bar appearanceet définissez-le sur NO.

Je pense que c'est ainsi que cela devrait être fait, PAS à partir du contrôleur de navigation, si vous voulez la même couleur de barre d'état pour toute l'application. Vous pouvez avoir des écrans qui ne sont pas nécessairement intégrés dans une sous-classe UINavigationControllerou une autre UINavigationControllersous-classe ailleurs, et d'autres choses.

EDIT : Vous pouvez également le faire sans taper de code: https://stackoverflow.com/a/18732865/855680

Matthieu Quiros
la source
1
Notez que cette méthode est obsolète à partir de IOS 9.0
Mohamed Salah
10

Dans viewDidLoad, écrivez simplement ceci

[self setNeedsStatusBarAppearanceUpdate];

fais juste ça et ça marchera

pouvez-vous s'il vous plaît essayer ceci

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

Une autre chose que j'ai vue dans votre question est que vous avez écrit la méthode comme ceci

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

mais ça devrait être comme ça

-(UIStatusBarStyle)preferredStatusBarStyle{ 
    return UIStatusBarStyleLightContent; 
} 
Utilisateur 1531343
la source
Cela provoque l'appel de la méthode favoriteStatusBarStyle, mais la barre d'état est toujours noire.
Andrew Smith
s'il vous plaît voir ma réponse mise à jour .. laissez-moi savoir rapidement si cela fonctionne ou non
Utilisateur 1531343
Ma question initiale dit explicitement que je dois faire un contrôle vue par vue de la barre d'état.
Andrew Smith
pouvez-vous s'il vous plaît vérifier votre code en référence à ma question mise à jour?
Utilisateur 1531343
1
[self setNeedsStatusBarAppearanceUpdate];une si bonne méthode, merci!
Supertecnoboff
6

Voici comment je l'ai résolu. Habituellement, le navigationController ou tabBarController sont ceux qui décident de l'apparence de la barre d'état (masqué, couleur, etc.).

J'ai donc fini par sous-classer le contrôleur de navigation et substituer preferStatusBarStyle. si le ViewContorller visible actuel implémente StatusBarStyleHandler, je demande que la valeur soit utilisée comme style, si ce n'est pas le cas, je renvoie simplement une valeur par défaut.

La façon dont vous déclenchez une mise à jour de l'apparence de la barre d'état consiste à appeler setNeedsStatusBarAppearanceUpdatequi déclenche à preferredStatusBarStylenouveau et met à jour l'interface utilisateur en fonction de ce que la méthode renvoie

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

Puis utilisation

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
la source
Cet article serait bien amélioré si vous pouviez expliquer pourquoi et comment cela résout le problème.
Au lieu de sous-classer UINavigationController, vous pouvez également simplement créer une extension pour UINavigationController et obtenir le même résultat sans avoir à sous-classer.
wilforeal
4

1) Un paramètre pour l'ensemble du projet:

Si disponible, supprimez la UIViewControllerBasedStatusBarAppearancepaire clé-valeur de votre info.plist ou définissez-la NOsans la supprimer. S'il n'est pas disponible dans votre info.plist, ne faites rien. La valeur par défaut est NOpour cette propriété.

Ajoutez le code ci-dessous à votre AppDelegate.m:

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

2) Différents paramètres pour différents contrôleurs de vue:

Ajoutez UIViewControllerBasedStatusBarAppearanceune paire clé-valeur à votre info.plist et définissez-la sur YES.

Si votre View Controller n'est pas intégré à Navigation Controller. Disons MyViewController. ajoutez simplement le code ci-dessous à votre fichier MyViewController.m. Si votre View Controller est intégré à Navigation Controller, créez une nouvelle classe Cocoa Touch et faites-en une sous-classe de UINavigationController. Disons MyNC. Sélectionnez la vue du contrôleur de navigation sur votre Storyboard, dans le volet droit; Utilitaires -> Inspecteur d'identité -> Classe personnalisée -> Classe, tapez "MyNC". Après avoir associé la vue Storyboard à votre Cocoa Touch Class "MyNC", ajoutez le code ci-dessous à votre MyNC.m:

- (BOOL)prefersStatusBarHidden {
    return NO;
}

-(UIStatusBarStyle)preferredStatusBarStyle {
    return UIStatusBarStyleLightContent;
}
Fatih Aksu
la source
Semble dans iOS9 UIViewControllerBasedStatusBarAppearance contient par défaut la valeur OUI, car j'avais besoin de l'ajouter manuellement dans .plist et de le définir sur NON pour fonctionner correctement.
Mohd Asim
4

Même avec toutes les réponses ici, je n'ai toujours pas trouvé la solution exacte pour moi, mais j'ai commencé par la réponse de Daniel. Ce que j'ai fini avec c'était:

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

dans les contrôleurs de navigation (similaire pour tab, juste selectedViewController). Et puis il respectera le:

override var preferredStatusBarStyle: UIStatusBarStyle {
     return .lightContent
}

Dans chaque contrôleur de vue, sauf si vous le définissez autrement. Je n'ai pas besoin d'appeler setNeedsStatusBarAppearanceUpdate()n'importe où, il se met simplement à jour lorsque vous arrivez à chaque contrôleur de vue.

Andrew Plummer
la source
2
J'ai fini avec la solution presque identique après avoir lutté avec cela pendant des heures.
Scott Jungwirth
À un moment donné, cela semble avoir été corrigé, le simple fait d'utiliser favoriteStatusBarStyle dans chaque VC fonctionne très bien pour moi maintenant.
Andrew Plummer
4

Solution (s) iOS 13

La réponse la plus votée utilise le code «hérité» 👎

La définition de la barStylepropriété est désormais (iOS 13+) considérée comme une «personnalisation héritée». Selon Apple ,

Dans iOS 13 et versions ultérieures, personnalisez votre barre de navigation à l'aide des propriétés standardAppearance, compactAppearance et scrollEdgeAppearance. Vous pouvez continuer à utiliser ces anciens accesseurs pour personnaliser directement l'apparence de votre barre de navigation, mais vous devez mettre à jour l'apparence des différentes configurations de barre vous-même.

Concernant votre tentative - Vous étiez sur la bonne voie!

UINavigationControllerest une sous-classe de UIViewController(qui savait 🙃)!

Par conséquent, lorsque vous présentez les contrôleurs de vue intégrés aux contrôleurs de navigation, vous ne présentez pas vraiment les contrôleurs de vue intégrés; vous présentez les contrôleurs de navigation! UINavigationController, en tant que sous-classe de UIViewController, hérite de preferredStatusBarStyleet childForStatusBarStyle, que vous pouvez définir comme vous le souhaitez.

L'une des méthodes suivantes devrait fonctionner:

  1. Remplacer preferredStatusBarStyledansUINavigationController

    • preferredStatusBarStyle( doc ) - Le style de barre d'état préféré pour le contrôleur de vue
    • Sous-classe ou étendre UINavigationController

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

      OU

      extension UINavigationController {
          open override var preferredStatusBarStyle: UIStatusBarStyle {
              .lightContent
          }
      }
  2. Remplacer childForStatusBarStyledansUINavigationController

    • childForStatusBarStyle( doc ) - Appelé lorsque le système a besoin du contrôleur de vue à utiliser pour déterminer le style de la barre d'état
    • Selon la documentation d'Apple,

      "Si votre contrôleur de vue conteneur dérive son style de barre d'état de l'un de ses contrôleurs de vue enfants, [remplacez cette propriété] et retournez ce contrôleur de vue enfant. Si vous renvoyez nil ou ne remplacez pas cette méthode, le style de barre d'état pour self est utilisé . Si la valeur de retour de cette méthode change, appelez la méthode setNeedsStatusBarAppearanceUpdate (). "

    • En d'autres termes, si vous n'implémentez pas la solution 3 ici, le système reviendra à la solution 2 ci-dessus.
    • Sous-classe ou étendre UINavigationController

      class MyNavigationController: UINavigationController {
          override var childForStatusBarStyle: UIViewController? {
              topViewController
          }
      }

      OU

      extension UINavigationController {    
          open override var childForStatusBarStyle: UIViewController? {
              topViewController
          }
      }
    • Vous pouvez retourner n'importe quel contrôleur de vue ci-dessus. Je recommande l'un des éléments suivants:

      • topViewController(of UINavigationController) ( doc ) - Le contrôleur de vue en haut de la pile de navigation
      • visibleViewController(of UINavigationController) ( doc ) - Le contrôleur de vue associé à la vue actuellement visible dans l'interface de navigation (indice: cela peut inclure "un contrôleur de vue qui a été présenté de manière modale au-dessus du contrôleur de navigation lui-même")

Remarque: si vous décidez de sous UINavigationController-classer, n'oubliez pas d'appliquer cette classe à vos contrôleurs de navigation via l'inspecteur d'identité dans IB.

PS Mon code utilise la syntaxe Swift 5.1 😎

Andrew Kirna
la source
1
Réponse très complète, merci! De plus, quelque chose que j'ai lutté pendant un certain temps, sur iOS 13, le .defaultstyle prend en compte le mode sombre et il n'est pas documenté, donc si vous prenez également en charge les versions iOS précédentes, vous pouvez ajouter if #available(iOS 13, *) { return .darkContent } else { return .default }si vous essayez de configurer le style de la barre d'état manuellement en fonction de la couleur derrière la barre d'état et cette couleur est "brillante".
valcanaia
1
Notez que la méthode d'extension de faire override var ne fonctionne plus dans Xcode 11.4 / iOS 13.4
Marc Etcheverry
Parce que l'extension des classes objectif C dans Swift est implémentée via les catégories Objective C. Le remplacement des méthodes par le biais des catégories de l'Objectif C n'est pas recommandé et risque de ne pas fonctionner. Voir stackoverflow.com/a/38274660/2438634
Marc Etcheverry
Bien que la substitution de UINavigationController fonctionne définitivement, cela ressemble à un bogue du côté Apple qui ne fait pas le childForStatusBarStyle par défaut en renvoyant son topViewController. Par exemple, UITabBarController fait cela pour ses onglets. Pour moi, il n'y a aucune raison pour que UINavigationController, étant strictement un contrôleur de conteneur pour héberger de "vrais" contrôleurs de vue plutôt que de présenter sa propre interface utilisateur, devrait "manger" ces styles de barre d'état. Va créer un radar pour Apple.
Igor Vasilev
1

Si vous vouliez masquer le statusBar pendant splashScreen mais que vous vouliez changer le style en contenu clair (StatusBarInitiallyHidden sur Plist doit être NO pour masquer statusBar sur splash), vous pouvez l'ajouter à la méthode didFinishLaunchingWithOptions de appDelegate pour passer à lightContent.

[[UIApplication sharedApplication]setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
[[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleLightContent];
Aalesano
la source
1

exemple rapide

dans AppDelegate.swift

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

    return true
}

dans info.plist set Afficher l'apparence de la barre d'état basée sur le contrôleur: NON

Fyalavuz
la source
1

Si vous utilisez NavigationController, vous pouvez sous-classe NavigationControllerafin qu'elle consulte son contrôleur de vue enfant

// 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
la source
1

Swift 4.2

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

extension UINavigationController {
    open override var childForStatusBarStyle: UIViewController? {
        return visibleViewController
    }
}
Vyacheslav
la source
Notez que la méthode d'extension de faire override var ne fonctionne plus dans Xcode 11.4 / iOS 13.4
Marc Etcheverry
@MarcEtcheverry alors, pourquoi avez-vous décliné la réponse? cela semble étrange.
Vyacheslav
Parce que l'extension des classes objectif C dans Swift est implémentée via les catégories Objective C. Le remplacement des méthodes par le biais des catégories de l'Objectif C n'est pas recommandé et risque de ne pas fonctionner. Voir stackoverflow.com/a/38274660/2438634
Marc Etcheverry
@MarcEtcheverry 'not recommended'! = 'Ne jamais l'utiliser!'. pour juillet 2018, la réponse était correcte. Même cette réponse n'est pas à jour, ce n'est pas une raison pour la rejeter. Je ne vois pas le futur
Vyacheslav
0

Vous pouvez définir le style de la barre d'état. Il ressemblera à la barre d'état comme IOS 6 et ci-dessous.
Collez ces méthodes dans votre contrôleur de vue

-(UIStatusBarStyle)preferredStatusBarStyle{
    return UIStatusBarStyleBlackOpaque;
}

et appelez cette méthode depuis la vue s'est chargée comme ceci

if([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f)
    {
       [self setNeedsStatusBarAppearanceUpdate];
    }
Ganapathie
la source
Voulez-vous dire [self setStatusBarNeedsUpdate]dans le deuxième bloc? (Ou autre chose au moins).
mxcl
0

Je veux juste ajouter une note pour un cas spécifique auquel j'ai été confronté. J'avais une autre UIWindow dans mon application pour afficher un visage de chat flottant partout dans mon application tout le temps. Cela n'a fait fonctionner aucune des solutions ci-dessus, et je ne sais pas vraiment pourquoi! Tout ce que j'ai remarqué, c'est que mon ViewController dans la nouvelle UIWindow en était la raison! Et si je voulais changer le style de la barre d'état, je dois le faire dans ce contrôleur de vue de la nouvelle UIWindow.

Cette note pourrait aider d'autres personnes qui ont une structure similaire! Donc, fondamentalement, vous pouvez appliquer les solutions mentionnées ci-dessus dans le ViewController de la nouvelle UIWindow.

Encore une fois, c'est un cas spécifique.

Merci

Ehab Saifan
la source
-1

Pour swift 3, dans votre UIViewController:

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