preferStatusBarStyle n'est pas appelé

257

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

trgoofi
la source
Il n'est pas appelé dans quel contexte: simulateur? sur un appareil?
bneely
@bneely les deux.
trgoofi
Vous utilisez le simulateur iOS 7, un appareil iOS 7 et votre SDK de base est 7.0?
bneely
@bneely iOS SDK 7.0 est affiché sous le nom de mon projet, cela signifie-t-il que mon SDK de base est 7.0?
trgoofi
Dans les paramètres de génération, "Base SDK" est l'endroit où la valeur est définie. Il semble que votre projet soit défini sur 7.0.
bneely

Réponses:

117

Cause racine possible

J'ai eu le même problème et j'ai compris que cela se produisait parce que je ne configurais pas le contrôleur de vue racine dans ma fenêtre d'application.

Le UIViewControllerdans lequel j'avais implémenté le a preferredStatusBarStyleété utilisé dans un UITabBarController, qui contrôlait l'apparence des vues à l'écran.

Lorsque j'ai défini le contrôleur de vue racine pour qu'il pointe vers cela UITabBarController, les modifications de la barre d'état ont commencé à fonctionner correctement, comme prévu (et la preferredStatusBarStyleméthode était 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)

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

[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

ou

[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];

Notez que vous aurez également besoin de jeu UIViewControllerBasedStatusBarAppearancepour NOdans le fichier plist si vous utilisez cette méthode.

AbdullahC
la source
2
J'ai le même problème que vous, ne définissant pas le contrôleur de vue racine. Comment diable avez-vous trouvé ça?
trgoofi
1
Je soupçonnais que quelque chose dans le cadre ne recevait pas la notification setNeedsStatusBarAppearanceUpdate- mes soupçons ont été confirmés lorsque j'ai apporté ce changement.
AbdullahC
2
Un problème connexe que j'ai trouvé dans une application était un contrôleur de vue avec un contrôleur de vue enfant en plein écran qui ne remplaçait pas childViewControllerForStatusBarStyle et childViewControllerForStatusBarHidden pour renvoyer ce contrôleur de vue enfant. Si vous avez votre propre hiérarchie de contrôleur de vue, vous devez fournir ces méthodes pour indiquer au système quel contrôleur de vue doit être utilisé pour déterminer le style de la barre d'état.
Jon Steinmetz
la définition du rootviewcontroller ne change rien. Vous devriez travailler avec le commentaire de Jon. Et soyez prudent lorsque vous appelez setneedsstatusbarappearanceUpdate. Vous devez l'appeler du parent pour travailler.
doozMen
1
@Hippo tu es génial !! Comment avez-vous découvert que c'était parce que vous n'aviez pas configuré rootviewcontroller?
ViruMax
1019

Pour toute personne utilisant un UINavigationController:

Le UINavigationControllerne transfère pas les preferredStatusBarStyleappels à 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 où se trouve la barre d'état et devrait donc en être responsable. Par conséquent, l'implémentation preferredStatusBarStyledans vos VC au sein d'un contrôleur de navigation ne fera rien - ils ne seront jamais appelés.

L'astuce consiste UINavigationControllerà savoir à quoi sert pour décider quoi retourner UIStatusBarStyleDefaultou UIStatusBarStyleLightContent. Il base cela sur son UINavigationBar.barStyle. La valeur par défaut ( UIBarStyleDefault) entraîne la UIStatusBarStyleDefaultbarre d'état de premier plan sombre . Et UIBarStyleBlackdonnera une UIStatusBarStyleLightContentbarre d'état.

TL; DR:

Si vous voulez UIStatusBarStyleLightContentsur une UINavigationControllerutilisation:

self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
Tyson
la source
59
Agréable! Notez que preferredStatusBarStylesera en fait appelé sur le contrôleur de vue enfant si vous masquez la barre de navigation (définie navigationBarHiddensur YES), exactement comme il convient.
Patrick Pijnappel
25
Merci pour cette réponse. Si vous souhaitez définir le style de barre pour toutes vos barres de navigation, appelez[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack]
Thomas Desert
15
Réponse parfaite. Aucune des autres réponses sur SO n'a pris en compte l'UINavigationController. 2 heures à me cogner la tête contre le clavier.
Ryan Alford
10
Félicitations à @Patrick pour avoir indiqué que navigationBarHiddenset to YESaura effectivement preferredStatusBarStyleappelé, et un avertissement à ceux qui pourraient tomber dessus: cela fonctionne avec navigationBarHidden, mais pas avec navigationBar.hidden!
jcaron
4
devrait être évident, mais vous avez également besoin que "Afficher l'apparence de la barre d'état basée sur le contrôleur" soit défini sur OUI dans Info.plist pour que cela fonctionne.
Code Baller
99

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

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

et que ceux-ci renvoient le UIViewController visible actuel. Cela permet au contrôleur de vue visible actuel de définir son propre style / visibilité préféré.

Voici un extrait de code complet pour cela:

Dans Swift:

extension UINavigationController {

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

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

Dans l'objectif-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 il est ensuite implémenté dans un UIViewController:

Dans Swift

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

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

Dans Objective-C

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

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

Enfin, assurez-vous que le plist de votre application n'a PAS "Afficher l'apparence de la barre d'état basée sur le contrôleur" sur NON. Supprimez cette ligne ou définissez-la sur OUI (qui, je crois, est la valeur par défaut maintenant pour iOS 7?)

serenn
la source
On dirait que ça return self.topViewController;marche pour moi, mais return self.visibleViewController;- pas
k06a
visibleViewController peut retourner le contrôleur modal actuellement présenté lorsque vous le supprimez. Ce qui est décevant. Utilisez topViewController.
Ben Sinclair
1
@ d.lebedev ok, mais je pense qu'aucun de ces problèmes ne s'applique ici. Vous n'avez pas besoin d'appeler supercette méthode et vous voulez réellement changer le comportement de tous les contrôleurs de ce type
ed '
1
cela ne fonctionne pas pour moi sur iOS 9.3. Je suppose que c'est le problème: ce problème est particulièrement important car de nombreuses classes Cocoa sont implémentées à l'aide de catégories. Une méthode définie par le framework que vous essayez de remplacer peut elle-même avoir été implémentée dans une catégorie et l'implémentation qui a la priorité n'est donc pas définie.
vikingosegundo
2
C'est faux et ça casse dans iOS 13.4. Parce que l'extension des classes d'objectifs C dans Swift est implémentée via des catégories d'objectifs C. Il n'est pas recommandé de remplacer les méthodes dans les catégories de l'objectif C et il est probable qu'elles se brisent. Voir stackoverflow.com/a/38274660/2438634
Marc Etcheverry
79

Pour tous ceux qui luttent toujours avec cela, cette simple extension dans swift devrait résoudre le problème pour vous.

extension UINavigationController {
    override open var childForStatusBarStyle: UIViewController? {
        return self.topViewController
    }
}
Alex Brown
la source
10
Vous méritez une médaille.
nikans
2
Merci beaucoup mec. Je retournais plutôt visibleViewController sans succès.
Fábio Salata
1
C'est de l'or. J'ai un contrôleur de navigation intégré dans une barre d'onglets et je viens de le jeter dans un fichier et maintenant je peux changer l'apparence de la barre d'état où je veux.
Vahid Amiri
2
C'est faux et ça casse dans iOS 13.4. Parce que l'extension des classes d'objectifs C dans Swift est implémentée via des catégories d'objectifs C. Il n'est pas recommandé de remplacer les méthodes dans les catégories de l'objectif C et il est probable qu'elles se brisent. Voir stackoverflow.com/a/38274660/2438634
Marc Etcheverry
1
@MarcEtcheverry cette instance particulière n'avait pas tort. Le fait est que les sous-classes d'autres objets / protocoles tels que UINavigationController n'avaient aucune implémentation préalable de ceux-ci pour entrer en conflit dans la répartition dynamique. Il n'y avait pas de valeurs par défaut ou d'implémentations dans les sous-classes réelles, c'est pourquoi c'était la façon la plus propre de l'implémenter dans une application sans créer une dépendance inutile (point). Malheureusement, 13.4 semble avoir changé ce comportement. Je suppose que dans les coulisses, ils ont un contrôle ou une implémentation qui est inexistant depuis des années .........
TheCodingArt
20

Mon application utilise trois: UINavigationController, UISplitViewController, UITabBarController, ceux - ci semblent tout ainsi prendre le contrôle de la barre d'état et causeront preferedStatusBarStylede ne pas être 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. J'aimerais qu'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
    }
}

Edit: mise à jour pour les modifications de l'API 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
la source
1
Ceci est la seule solution qui fonctionne. Toutes les réponses sur SO pointent vers la solution standard qui ne fonctionnera pour aucune application avec NavigationControllers. Je vous remercie!!!
Houman
L'utilisation d'extensions pour remplacer est tout simplement erronée. Ce n'est pas sûr. Il existe plusieurs solutions plus simples. Utilisez plutôt une sous-classe.
Sulthan
2
C'est faux et ça casse dans iOS 13.4. Parce que l'extension des classes d'objectifs C dans Swift est implémentée via des catégories d'objectifs C. Il n'est pas recommandé de remplacer les méthodes dans les catégories de l'objectif C et il est probable qu'elles se brisent. Voir stackoverflow.com/a/38274660/2438634
Marc Etcheverry
1
@MarcEtcheverry cette instance particulière n'avait pas tort. Le fait est que les sous-classes d'autres objets / protocoles tels que UINavigationController n'avaient aucune implémentation préalable de ceux-ci pour entrer en conflit dans la répartition dynamique. Il n'y avait pas de valeurs par défaut ou d'implémentations dans les sous-classes réelles, c'est pourquoi c'était la façon la plus propre de l'implémenter dans une application sans créer une dépendance inutile (point). Malheureusement, 13.4 semble avoir changé ce comportement. Je suppose que dans les coulisses, ils ont un contrôle ou une implémentation qui est inexistant depuis des années .........
TheCodingArt
15

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, AppDelegateutilisez le code ci-dessous et écrivez-le dans la AppDelegate's didFinishLaunchingWithOptionsméthode.

Et ne pas oublier de mettre l' UIViewControllerBasedStatusBarAppearanceau YESdans le fichier .plist, sinon le changement ne reflètent pas nécessairement.

Code

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

     return YES;
}
Yogesh Suthar
la source
14

Sur un UINavigationController, preferredStatusBarStylen'est pas appelé car il topViewControllerest préférable de le faire self. Donc, pour être preferredStatusBarStyleappelé sur un UINavigationController, vous devez changer son childViewControllerForStatusBarStyle.

Recommandation

Remplacez votre UINavigationController dans votre classe:

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

Alternative non recommandée

Pour le faire pour tous les UINavigationController, vous pouvez remplacer une extension (avertissement: cela affecte UIDocumentPickerViewController, UIImagePickerController, etc.), mais vous ne devriez probablement pas le faire selon la documentation Swift :

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

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

presentedViewController.modalPresentationCapturesStatusBarAppearance = true

N'oubliez pas de remplacer également le preferredStatusBarStyledans le contrôleur de vue présenté.

frin
la source
9

Un ajout à la réponse d'Hippo: si vous utilisez un UINavigationController, 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.

Artem Abramov
la source
Ne faites pas cela, cela fonctionne pour l'instant mais peut briser le comportement futur. Il suffit de changer le style navBar - voir ma réponse stackoverflow.com/a/19513714/505457
Tyson
2
Vous devez utiliser une sous-classe, pas une catégorie.
shuiyouren
2Tyson: Pourquoi rompra-t-il les comportements futurs? PreferredStatusBarStyle: est la méthode préférée d'Apple pour configurer le style de la barre d'état.
Artem Abramov
2shuiyouren: Pourquoi devrais-je augmenter la complexité en sous-classant si je peux simplement utiliser une catégorie et l'inclure à chaque endroit où je le souhaite? Quoi qu'il en soit, c'est une question d'architecture, pas d'implémentation.
Artem Abramov
2
@ArtemAbramov Parce que UINavigationController implémente déjà preferredStatusBarStyleet fait la logique spécifique UINavigationController. À l'heure actuelle, cette logique est basée sur, navigationBar.barStylemais je peux voir des vérifications supplémentaires ajoutées (par exemple, UISearchDisplayControllerpasser au mode masquer la barre de navigation). En remplaçant la logique par défaut, vous perdez toutes ces fonctionnalités et vous laissez ouvert pour les moments gênants «wtf» à l'avenir. Voir ma réponse ci-dessus pour la bonne façon de le faire tout en prenant en charge le comportement du contrôleur de navigation intégré.
Tyson
9

Swift 4.2 et supérieur

Comme mentionné dans la réponse sélectionnée , la cause principale 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 fenêtre

    Votre contrôleur de vue racine de fenêtre est un objet UIViewController et il ajoute ou supprime en outre le contrôleur de navigation ou tabController en fonction de votre flux d'application.

    Ce type de flux est généralement utilisé si votre application a un flux de pré-connexion sur la pile de navigation sans onglets et un flux de connexion de post avec des onglets et éventuellement chaque onglet contient un contrôleur de navigation.

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

    Il s'agit du flux dans lequel le contrôleur de vue racine de fenêtre est tabBarController, éventuellement chaque onglet contient en outre le contrôleur de navigation.

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

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

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

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 UIViewControllerBasedStatusBarAppearancesaisir la clé info.plistcar c'est vrai 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 ajoutez un nouveau contrôleur de navigation ou tabBar à NewFlowUIViewController, puis ajoutez l'extension de NewFlowUIViewControllerainsi pour gérer davantage le style de la barre d'état du contrôleur.

  • Si vous définissez modalPresentationStyle autrement que fullScreenlors de la présentation modale, vous devez définir modalPresentationCapturesStatusBarAppearancetrue pour que le contrôleur de vue présenté reçoive le contrôle d'apparence de la barre d'état.

abhimanyu jindal
la source
Excellente réponse!
Amin Benarieb
3
C'est faux et ça casse dans iOS 13.4. Parce que l'extension des classes d'objectifs C dans Swift est implémentée via des catégories d'objectifs C. Il n'est pas recommandé de remplacer les méthodes dans les catégories de l'objectif C et il est probable qu'elles se brisent. Voir stackoverflow.com/a/38274660/2438634
Marc Etcheverry
@MarcEtcheverry cette instance particulière n'avait pas tort. Le fait est que les sous-classes d'autres objets / protocoles tels que UINavigationController n'avaient aucune implémentation préalable de ceux-ci pour entrer en conflit dans la répartition dynamique. Il n'y avait pas de valeurs par défaut ou d'implémentations dans les sous-classes réelles, c'est pourquoi c'était la façon la plus propre de l'implémenter dans une application sans créer une dépendance inutile (point). Malheureusement, 13.4 semble avoir changé ce comportement. Je suppose que dans les coulisses, ils ont un contrôle ou une implémentation qui est inexistant depuis des années .........
TheCodingArt
8

Solution (s) iOS 13

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

Par conséquent, lorsque vous présentez des contrôleurs de vue intégrés dans les 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 childForStatusBarStyleque vous pouvez définir à votre guise.

L'une des méthodes suivantes devrait fonctionner:

  1. Désactiver complètement le mode sombre
    • Dans votre info.plist, ajoutez la propriété suivante:
      • Clé - UIUserInterfaceStyle(alias "Style d'interface utilisateur")
      • Valeur - Léger
  2. Remplacer à l' preferredStatusBarStyleintérieurUINavigationController

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

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

      OU

      extension UINavigationController {
          open override var preferredStatusBarStyle: UIStatusBarStyle {
              .lightContent
          }
      }
  3. Remplacer à l' childForStatusBarStyleintérieurUINavigationController

    • 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 de conteneur dérive son style de barre d'état de l'un de ses contrôleurs de vue enfant, [remplacez cette propriété] et renvoyez ce contrôleur de vue enfant. Si vous retournez 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 extension 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 que vous souhaitez ci-dessus. Je recommande l'une des options suivantes:

      • 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 façon modale au-dessus du contrôleur de navigation lui-même")

Remarque: Si vous décidez de sous UINavigationController- classe , 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
Ma barre d'état devient noire après la rotation de l'écran. Une idée pourquoi? Cela ne se produit que sur le simulateur iPad Pro.
Pedro Paulo Amorim
@PedroPauloAmorim, pouvez-vous fournir plus d'informations? Comment le contrôleur de vue de dessus est-il présenté (modal, plein écran, spectacle)? Est-il imbriqué dans un contrôleur de navigation? Le texte devient-il noir ou l'arrière-plan aussi? Qu'est-ce que vous essayez d'accomplir?
Andrew Kirna
J'ai défini la barre d'état lumineuse dans toute mon application. Il s'éclaire en deux rotations, dans le troisième il s'assombrit et ne revient jamais à la lumière, forçant même à le redessiner. Cela se passe sur le simulateur iPad Pro. Les vues sont présentées en plein écran et ne sont pas imbriquées dans un contrôleur de navigation. Seul le texte devient sombre.
Pedro Paulo Amorim
Comment définissez-vous la barre d'état de l'éclairage en premier lieu?
Andrew Kirna
3
Votre remplacement via une extension n'est pas un véritable remplacement. C'est une mauvaise utilisation dangereuse de la langue. Cela peut se casser très facilement.
Sulthan
7

La réponse de @ serenn ci-dessus est toujours excellente pour le cas de UINavigationControllers. Cependant, pour swift 3, les fonctions childViewController ont été remplacées par vars. Le UINavigationControllercode d'extension doit donc ê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
}
John Stricker
la source
2
C'est faux et ça casse dans iOS 13.4. Parce que l'extension des classes d'objectifs C dans Swift est implémentée via des catégories d'objectifs C. Il n'est pas recommandé de remplacer les méthodes dans les catégories de l'objectif C et il est probable qu'elles se brisent. Voir stackoverflow.com/a/38274660/2438634
Marc Etcheverry
@MarcEtcheverry cette instance particulière n'avait pas tort. Le fait est que les sous-classes d'autres objets / protocoles tels que UINavigationController n'avaient aucune implémentation préalable de ceux-ci pour entrer en conflit dans la répartition dynamique. Il n'y avait pas de valeurs par défaut ou d'implémentations dans les sous-classes réelles, c'est pourquoi c'était la façon la plus propre de l'implémenter dans une application sans créer une dépendance inutile (point). Malheureusement, 13.4 semble avoir changé ce comportement. Je suppose que dans les coulisses, ils ont un contrôle ou une implémentation qui est inexistant depuis des années .........
TheCodingArt
6

Si votre viewController est sous UINavigationController.

Sous-classe UINavigationController et ajouter

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

ViewController preferredStatusBarStylesera appelé.

PowHu
la source
4

UIStatusBarStyle dans iOS 7

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

Le style de la barre d'état fait référence à l'apparence de son contenu. Dans iOS 7, le contenu de la barre d'état est sombre ( UIStatusBarStyleDefault) ou clair ( UIStatusBarStyleLightContent). Les deux UIStatusBarStyleBlackTranslucentet UIStatusBarStyleBlackOpaquesont déconseillés dans iOS 7.0. UtilisationUIStatusBarStyleLightContent plutôt.

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 barre de navigation est UIBarStyleDefault, le style de barre d'état sera UIStatusBarStyleDefault; si le style de barre de navigation est UIBarStyleBlack, le style de barre d'état seraUIStatusBarStyleLightContent .

S'il n'y a pas de barre de navigation sous 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. Il peut être remplacé 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 soi, 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 -[UIApplication statusBarStyle]méthode, ajoutez la UIViewControllerBasedStatusBarAppearanceclé au Info.plistfichier d' une application et donnez-lui la valeur NO.

oscarr
la source
3

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 à UINavigationController comme celle-ci dans Swift 3 et elle s'appliquera à tous les contrôleurs de navigation (au lieu de l'assigner à un contrôleur à un temps).

extension UINavigationController {

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

        self.navigationBar.barStyle = UIBarStyle.black
    }

}
Benjamin Lowry
la source
1
Mais que faire si ma barre de navigation est masquée?
Slavcho
1
Parce que j'ai besoin que la navigation soit cachée et que la barre d'état soit visible.
Slavcho
1

Dans Swift pour tout type de UIViewController:

Dans votre AppDelegateset:

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

myRootControllerpeut être tout type de UIViewController, par exemple UITabBarControllerou UINavigationController.

Ensuite, remplacez ce contrôleur racine comme ceci:

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

Cela changera 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 appearancesur YES dans votre Info.plistpour que cela fonctionne (ce qui est la valeur par défaut).

Damnum
la source
@Comment ça se passe dans swift3?
avion
1

Solution Swift 3 iOS 10:

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

La plupart des réponses n'incluent pas une bonne implémentation de la childViewControllerForStatusBarStyleméthode pour UINavigationController. Selon mon expérience, vous devez gérer des cas tels que lorsque le contrôleur de vue transparent est présenté sur le contrôleur de navigation. Dans ces cas, vous devez 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
}
Timur Bernikovich
la source
1

Dans mon cas, j'ai accidentellement présenté le contrôleur View / Navigation comme UIModalPresentationStyle.overFullScreen, ce qui fait que je ne suis preferredStatusBarStylepas appelé. Après l'avoir réactivé UIModalPresentationStyle.fullScreen, tout fonctionne.

Casey
la source
1

Quant à iOS 13.4, la preferredStatusBarStyleméthode dans la UINavigationControllercatégorie ne sera pas appelée, le swizzling semble être la seule option sans avoir besoin d'utiliser une sous-classe.

Exemple:

En-tête de catégorie:

@interface UINavigationController (StatusBarStyle)
+ (void)setUseLightStatusBarStyle;
@end

La mise en oeuvre:

#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

Utilisation dans AppDelegate.h:

#import "UINavigationController+StatusBarStyle.h"

[UINavigationController setUseLightStatusBarStyle];
Kevin Flachsmann
la source
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ée 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 mettre en œuvre le reste des méthodes ( showsStatusBar , animatesStatusBarVisibility , prefferedStatusBarAnimation ) du protocole et UIViewController + Upgrade fera la personnalisation appropriée en fonction des valeurs fournies par eux.

arturgrigor
la source
0

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
}
Tai Le
la source
0

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

assurez-vous d'aller dans votre liste et de régler "Afficher l'apparence de la barre d'état basée sur le contrôleur" sur OUI. Si ce n'est pas le cas, cela ne fonctionnera pas.

Richard Garfield
la source
Définir UIViewControllerBasedStatusBarAppearance sur YES dans la liste de projets a fait toute la différence pour moi. Je l'avais oublié.
filo
0

Depuis Xcode 11.4, remplacer le preferredStatusBarStyle propriété dans une extension UINavigationController ne fonctionne plus car elle ne sera pas appelée.

Réglage du barStylede navigationBarà des .blackœuvres , certes , mais cela va ajouter des effets secondaires indésirables si vous ajoutez subviews à la navigationBar qui peut avoir différentes apparences pour le mode lumière et l' obscurité. Parce qu'en définissant le barStylesur noir, la userInterfaceStylevue qui est intégrée dans la barre de navigation aura alors toujours userInterfaceStyle.darkindépendamment userInterfaceStylede l'application.

La bonne solution que je trouve est d'ajouter une sous-classe de UINavigationControlleret de la remplacer preferredStatusBarStyle. Si vous utilisez ensuite ce UINavigationController personnalisé pour toutes vos vues, vous serez du côté de la sauvegarde.

PatrickDotStar
la source
-1

Le NavigationController ou TabBarController sont ceux qui doivent fournir le style. Voici comment j'ai résolu: https://stackoverflow.com/a/39072526/242769

aryaxt
la source
Si vous pensez qu'il s'agit d'un doublon d'une autre question, veuillez le fermer en tant que doublon