web-dev-qa-db-fra.com

preferredStatusBarStyle n'est pas appelé

J'ai suivi ce fil pour remplacer -preferredStatusBarStyle, mais ce n'est pas appelé. Y a-t-il des options que je peux changer pour l'activer? (J'utilise des XIB dans mon projet.)

224
trgoofi

Cause fondamentale possible

J'ai eu le même problème et j'ai compris que c'était parce que je ne configurais pas le contrôleur de vue racine dans la fenêtre de l'application. 

La UIViewController dans laquelle j'avais implémenté la preferredStatusBarStyle était utilisée dans une UITabBarController, qui contrôlait l'affichage des vues à l'écran.

Lorsque je configure le contrôleur de vue racine pour qu'il pointe vers cette UITabBarController, les modifications de la barre d'état ont commencé à fonctionner correctement, comme prévu (et la méthode preferredStatusBarStyle a été appelée).

(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ... // other view controller loading/setup code

    self.window.rootViewController = rootTabBarController;
    [self.window makeKeyAndVisible];
    return YES;
}

Méthode alternative (obsolète dans iOS 9)

Vous pouvez également appeler l'une des méthodes suivantes, selon le cas, dans chacun de vos contrôleurs de vue, en fonction de la couleur d'arrière-plan, au lieu de devoir utiliser setNeedsStatusBarAppearanceUpdate:

[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

ou

[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];

Notez que vous devez également définir UIViewControllerBasedStatusBarAppearance sur NO dans le fichier plist si vous utilisez cette méthode.

112
AbdullahC

Pour toute personne utilisant un contrôleur UINavigation:

La UINavigationController ne transfère pas les appels preferredStatusBarStyle à ses contrôleurs de vue enfant. Au lieu de cela, il gère son propre état - comme il se doit, il dessine en haut de l'écran l'emplacement de la barre d'état. Par conséquent, implémenter preferredStatusBarStyle dans vos VCs dans un contrôleur de navigation ne fera rien - ils ne seront jamais appelés.

L’astuce consiste à utiliser ce que UINavigationController utilise pour décider quoi retourner pour UIStatusBarStyleDefault ou UIStatusBarStyleLightContent. Il se base sur son UINavigationBar.barStyle. La valeur par défaut (UIBarStyleDefault) entraîne l'affichage de la barre d'état UIStatusBarStyleDefault au premier plan sombre. Et UIBarStyleBlack donnera une barre UIStatusBarStyleLightContent.

TL; DR:

Si vous voulez UIStatusBarStyleLightContent sur une UINavigationController, utilisez:

self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
954
Tyson

J'ai donc ajouté une catégorie à UINavigationController, mais j'ai utilisé les méthodes suivantes: 

-(UIViewController *)childViewControllerForStatusBarStyle;
-(UIViewController *)childViewControllerForStatusBarHidden;

et ont renvoyé le UIViewController visible en cours. Cela permet au contrôleur de vue visible actuel de définir son propre style/visibilité.

Voici un extrait de code complet pour cela:

En rapide:

extension UINavigationController {

    public override func childViewControllerForStatusBarHidden() -> UIViewController? {
        return self.topViewController
    }

    public override func childViewControllerForStatusBarStyle() -> UIViewController? {
        return self.topViewController
    }
}

En Objective-C:

@interface UINavigationController (StatusBarStyle)

@end

@implementation UINavigationController (StatusBarStyle)

-(UIViewController *)childViewControllerForStatusBarStyle {
    return self.topViewController;
}

-(UIViewController *)childViewControllerForStatusBarHidden {
    return self.topViewController;
}

@end

Et pour faire bonne mesure, voici comment cela est implémenté dans un UIViewController:

En rapide

override public func preferredStatusBarStyle() -> UIStatusBarStyle {
    return .LightContent
}

override func prefersStatusBarHidden() -> Bool {
    return false
}

En Objective-C

-(UIStatusBarStyle)preferredStatusBarStyle {
    return UIStatusBarStyleLightContent; // your own style
}

- (BOOL)prefersStatusBarHidden {
    return NO; // your own visibility code
}

Enfin, assurez-vous que la pliste de votre application ET QUE ne présente pas «L'apparence de la barre d'état basée sur le contrôleur d'affichage» définie sur NO. Supprimez cette ligne ou définissez-la sur OUI (qui, à mon avis, est la valeur par défaut pour iOS 7?)

90
serenn

Pour ceux qui ont encore du mal à résoudre ce problème, cette simple extension de Swift devrait résoudre le problème pour vous.

extension UINavigationController {
    override open var childViewControllerForStatusBarStyle: UIViewController? {
        return self.topViewController
    }
}
51
Alex Brown

La réponse de Tyson est correcte pour changer la couleur de la barre d'état en blanc dans UINavigationController.

Si quelqu'un veut obtenir le même résultat en écrivant le code dans AppDelegate, utilisez le code ci-dessous et écrivez-le dans la méthode AppDelegate'sdidFinishLaunchingWithOptions.

Et n'oubliez pas de définir UIViewControllerBasedStatusBarAppearance sur YES dans le fichier .plist, sinon la modification ne sera pas reflétée.

Code

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

     return YES;
}
14
Yogesh Suthar

Un ajout à la réponse d'Hippo: si vous utilisez un contrôleur UINavigation, il est probablement préférable d'ajouter une catégorie:

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

Cette solution est probablement meilleure que de passer à un comportement bientôt obsolète.

9
Artem Abramov

Mon application utilisait les trois: UINavigationController, UISplitViewController, UITabBarController, de sorte qu'ils semblent tous prendre le contrôle de la barre d'état et feront en sorte que preferedStatusBarStyle ne sera pas appelé pour leurs enfants. Pour remplacer ce comportement, vous pouvez créer une extension comme le reste des réponses l’ont mentionné. Voici une extension pour les trois, dans Swift 4. Souhaitez que Apple soit plus clair sur ce genre de choses.

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

Sur un contrôleur UINavigation, preferredStatusBarStyle n'est pas appelé car sa topViewController est préférée à self. Ainsi, pour que preferredStatusBarStyle soit appelé sur un UINavigationController, vous devez changer sa childViewControllerForStatusBarStyle.

Pour le faire pour un contrôleur UINavigation (ma recommandation):

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

Pour le faire pour tous les UINavigationController (avertissement: cela affecte UIDocumentPickerViewController, UIImagePickerController, etc.):

extension UINavigationController {
    open override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return nil
    }
}
7
Cœur

@ serenn's answer above est toujours une excellente solution pour le cas des contrôleurs UINavigation. Cependant, pour Swift 3, les fonctions childViewController ont été remplacées par vars. Donc, le code d'extension UINavigationController devrait être: 

override open var childViewControllerForStatusBarStyle: UIViewController? {
  return topViewController
}

override open var childViewControllerForStatusBarHidden: UIViewController? {
  return topViewController
}

Et puis dans le contrôleur de vue qui devrait dicter le style de la barre d'état: 

override var preferredStatusBarStyle: UIStatusBarStyle {
   return .lightContent
}
7
John Stricker

Si votre viewController est sous UINavigationController.

Sous-classe UINavigationController et ajouter

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

La variable preferredStatusBarStyle de ViewController sera appelée.

5
PowHu

Swift 4.2 et au-dessus

Comme mentionné dans réponse sélectionnée , la cause première est de vérifier votre objet Contrôleur de vue racine de fenêtre.

Cas possibles de votre structure de flux

  • L'objet UIViewController personnalisé est le contrôleur de vue racine de la fenêtre

    Votre contrôleur d’affichage racine de la fenêtre est un objet UIViewController. Il ajoute ou supprime en outre le contrôleur de navigation ou tabController en fonction du flux de votre application.

    Ce type de flux est généralement utilisé si votre application dispose d'un flux de connexion préalable sur la pile de navigation sans onglets et après le flux de connexion avec des onglets et, éventuellement, chaque onglet contient le contrôleur de navigation.

  • L'objet TabBarController est le contrôleur de la vue racine de la fenêtre

    Il s'agit du flux dans lequel le contrôleur de vue racine de la fenêtre est tabBarController. Chaque onglet contient également le contrôleur de navigation.

  • L'objet NavigationController est le contrôleur de vue de la fenêtre en fond de fenêtre

    Il s'agit du flux dans lequel le contrôleur de vue racine de la fenêtre est navigationController.

    Je ne sais pas s’il est possible d’ajouter un contrôleur de barre d’onglet ou un nouveau contrôleur de navigation dans un contrôleur de navigation existant. Mais si tel est le cas, nous devons passer le contrôle de style de la barre d'état au conteneur suivant. Donc, j'ai ajouté le même contrôle dans l'extension UINavigationController pour trouver childForStatusBarStyle

Utilisez les extensions suivantes, il gère tous les scénarios ci-dessus -

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
    }
}
  • Vous n'avez pas besoin de la touche UIViewControllerBasedStatusBarAppearance dans info.plist car elle est vraie par défaut

Points à considérer pour des flux plus complexes

  • Si vous présentez un nouveau flux de manière modale, il se détache du flux de style de barre d’état existant. Supposons donc que vous présentiez un NewFlowUIViewController, puis que vous ajoutiez un nouveau contrôleur de navigation ou de tabulation à NewFlowUIViewController, puis que vous ajoutiez l'extension de NewFlowUIViewController pour gérer davantage le style de barre d'état du contrôleur de vue.

  • Si vous définissez modalPresentationStyle autre que fullScreen lors d'une présentation modale, vous devez définir modalPresentationCapturesStatusBarAppearance sur true, de sorte que le contrôleur de vue présenté doit recevoir le contrôle de l'apparence de la barre d'état.

5
abhimanyu jindal

UIStatusBarStyle dans iOS 7

La barre d'état dans iOS 7 est transparente, la vue derrière elle est visible.

Le style de la barre d'état fait référence aux apparences de son contenu. Dans iOS 7, le contenu de la barre d'état est sombre (UIStatusBarStyleDefault) ou clair (UIStatusBarStyleLightContent). UIStatusBarStyleBlackTranslucent et UIStatusBarStyleBlackOpaque sont obsolètes dans iOS 7.0. Utilisez UIStatusBarStyleLightContent à la place.

Comment changer UIStatusBarStyle

Si sous la barre d'état se trouve une barre de navigation, le style de la barre d'état sera ajusté pour correspondre au style de la barre de navigation (UINavigationBar.barStyle):

Plus précisément, si le style de la barre de navigation est UIBarStyleDefault, le style de la barre d'état sera UIStatusBarStyleDefault; Si le style de la barre de navigation est UIBarStyleBlack, le style de la barre d'état sera UIStatusBarStyleLightContent.

S'il n'y a pas de barre de navigation en dessous de la barre d'état, le style de la barre d'état peut être contrôlé et modifié par un contrôleur de vue individuel pendant l'exécution de l'application.

-[UIViewController preferredStatusBarStyle] est une nouvelle méthode ajoutée dans iOS 7. Vous pouvez le remplacer pour renvoyer le style de barre d'état préféré:

- (UIStatusBarStyle)preferredStatusBarStyle
  {
      return UIStatusBarStyleLightContent;
  }

Si le style de la barre d'état doit être contrôlé par un contrôleur de vue enfant au lieu de lui-même, remplacez -[UIViewController childViewControllerForStatusBarStyle] pour renvoyer ce contrôleur de vue enfant.

Si vous préférez désactiver ce comportement et définir le style de la barre d'état à l'aide de la méthode -[UIApplication statusBarStyle], ajoutez la clé UIViewControllerBasedStatusBarAppearance au fichier Info.plist de l'application et attribuez-lui la valeur NO.

4
oscarr

Si quelqu'un utilise un contrôleur de navigation et souhaite que tous ses contrôleurs de navigation aient le style noir, vous pouvez écrire une extension de UINavigationController comme celle-ci dans Swift 3, qui s'appliquera à tous les contrôleurs de navigation (au lieu de l'attribuer à un contrôleur à la fois). temps).

extension UINavigationController {

    override open func viewDidLoad() {
        super.viewDidLoad()

        self.navigationBar.barStyle = UIBarStyle.black
    }

}
4
Benjamin Lowry

En plus de la réponse de serenn, si vous présentez un contrôleur de vue avec une modalPresentationStyle (par exemple .overCurrentContext), vous devez également appeler cela sur le nouveau contrôleur de vue présenté:

presentedViewController.modalPresentationCapturesStatusBarAppearance = true

N'oubliez pas de remplacer également la variable preferredStatusBarStyle dans le contrôleur de vue présenté.

2
frin

Solution Swift 3 iOS 10:

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

Si quelqu'un rencontre ce problème avec UISearchController ..__, créez simplement une nouvelle sous-classe de UISearchController, puis ajoutez le code ci-dessous dans cette classe:

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

Dans Swift pour tout type de UIViewController:

Dans votre ensemble AppDelegate:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    window!.rootViewController = myRootController
    return true
}

myRootController peut être n’importe quel type de UIViewController, par ex. UITabBarController ou UINavigationController.

Ensuite, remplacez ce contrôleur racine comme ceci:

class RootController: UIViewController {
    override func preferredStatusBarStyle() -> UIStatusBarStyle {
        return .LightContent
    }
}

Cela modifiera l'apparence de la barre d'état dans l'ensemble de votre application, car le contrôleur racine est seul responsable de l'apparence de la barre d'état.

N'oubliez pas de définir la propriété View controller-based status bar appearance sur YES dans votre Info.plist pour que cela fonctionne (valeur par défaut).

0
Damnum

La plupart des réponses n'incluent pas une bonne implémentation de la méthode childViewControllerForStatusBarStyle pour UINavigationController. Selon mon expérience, vous devriez gérer des cas tels que ceux où le contrôleur de vue transparent est présenté sur le contrôleur de navigation. Dans ces cas, vous devriez passer le contrôle à votre contrôleur modal (visibleViewController), mais pas lorsqu'il disparaît.

override var childViewControllerForStatusBarStyle: UIViewController? {
  var childViewController = visibleViewController
  if let controller = childViewController, controller.isBeingDismissed {
    childViewController = topViewController
  }
  return childViewController?.childViewControllerForStatusBarStyle ?? childViewController
}
0

Voici ma méthode pour résoudre ce problème.

Définissez un protocole appelé AGViewControllerAppearance .

AGViewControllerAppearance.h

#import <Foundation/Foundation.h>

@protocol AGViewControllerAppearance <NSObject>

@optional

- (BOOL)showsStatusBar;
- (BOOL)animatesStatusBarVisibility;
- (UIStatusBarStyle)preferredStatusBarStyle;
- (UIStatusBarAnimation)prefferedStatusBarAnimation;

@end

Définissez une catégorie sur UIViewController appelé 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

Maintenant, il est temps de dire que votre contrôleur de vue implémente le protocole AGViewControllerAppearance .

Exemple:

@interface XYSampleViewController () <AGViewControllerAppearance>

... the rest of the interface

@end

Bien sûr, vous pouvez implémenter le reste des méthodes ( showsStatusBar , animatesStatusBarVisibility , prefferedStatusBarAnimation ) à partir du protocole et UIViewController + Upgrade fera les opérations appropriées sur les valeurs fournies par eux.

0
arturgrigor

Notez que lorsque vous utilisez la solution self.navigationController.navigationBar.barStyle = UIBarStyleBlack;

assurez-vous de vous rendre sur votre plist et de définir "Afficher l'apparence de la barre d'état basée sur le contrôleur" sur OUI. Si c'est NON cela ne fonctionnera pas. 

0
Richard Garfield