Eu segui este tópico para substituir -preferredStatusBarStyle
, mas não é chamado. Existem opções que eu posso alterar para habilitá-lo? (Estou usando XIBs no meu projeto.)
Eu segui este tópico para substituir -preferredStatusBarStyle
, mas não é chamado. Existem opções que eu posso alterar para habilitá-lo? (Estou usando XIBs no meu projeto.)
Respostas:
Eu tinha o mesmo problema e descobri que estava acontecendo porque não estava configurando o controlador de exibição raiz na janela do meu aplicativo.
O UIViewController
em que eu havia implementado o preferredStatusBarStyle
foi usado em um UITabBarController
, que controlava a aparência das visualizações na tela.
Quando defino o controlador de exibição raiz para apontar para isso UITabBarController
, as alterações da barra de status começaram a funcionar corretamente, conforme o esperado (e o preferredStatusBarStyle
método estava sendo chamado).
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
... // other view controller loading/setup code
self.window.rootViewController = rootTabBarController;
[self.window makeKeyAndVisible];
return YES;
}
Como alternativa, você pode chamar um dos seguintes métodos, conforme apropriado, em cada um dos controladores de exibição, dependendo da cor de fundo, em vez de precisar usar setNeedsStatusBarAppearanceUpdate
:
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
ou
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];
Note que você também precisará conjunto UIViewControllerBasedStatusBarAppearance
para NO
no arquivo plist se você usar este método.
setNeedsStatusBarAppearanceUpdate
- minhas suspeitas foram confirmadas quando fiz essa alteração.
Para quem usa um UINavigationController:
O UINavigationController
não encaminha as preferredStatusBarStyle
chamadas para seus controladores de exibição filho. Em vez disso, ele gerencia seu próprio estado - como deveria, está desenhando na parte superior da tela onde fica a barra de status e, portanto, deve ser responsável por ela. A implementação preferredStatusBarStyle
em seus VCs dentro de um controlador nav não fará nada - eles nunca serão chamados.
O truque é quais são os UINavigationController
usos para decidir para o que retornar UIStatusBarStyleDefault
ou UIStatusBarStyleLightContent
. Ele baseia isso no seu UINavigationBar.barStyle
. O padrão ( UIBarStyleDefault
) resulta na UIStatusBarStyleDefault
barra de status escura do primeiro plano . E UIBarStyleBlack
dará uma UIStatusBarStyleLightContent
barra de status.
TL; DR:
Se você quiser UIStatusBarStyleLightContent
em um UINavigationController
uso:
self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
preferredStatusBarStyle
, de fato, será chamado no controlador de exibição filho se você ocultar a barra de navegação (definida navigationBarHidden
como YES
), exatamente como apropriado.
[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack]
navigationBarHidden
conjunto como YES
realmente preferredStatusBarStyle
ligou e um aviso para aqueles que podem tropeçar nisso: funciona com navigationBarHidden
, mas não com navigationBar.hidden
!
Então, na verdade, adicionei uma categoria ao UINavigationController, mas usei os métodos:
-(UIViewController *)childViewControllerForStatusBarStyle;
-(UIViewController *)childViewControllerForStatusBarHidden;
e eles devolveram o atual UIViewController visível. Isso permite que o atual controlador de exibição visível defina seu próprio estilo / visibilidade preferido.
Aqui está um trecho de código completo para ele:
Em Swift:
extension UINavigationController {
public override func childViewControllerForStatusBarHidden() -> UIViewController? {
return self.topViewController
}
public override func childViewControllerForStatusBarStyle() -> UIViewController? {
return self.topViewController
}
}
No Objetivo-C:
@interface UINavigationController (StatusBarStyle)
@end
@implementation UINavigationController (StatusBarStyle)
-(UIViewController *)childViewControllerForStatusBarStyle {
return self.topViewController;
}
-(UIViewController *)childViewControllerForStatusBarHidden {
return self.topViewController;
}
@end
E, para uma boa medida, veja como é implementado em um UIViewController:
Na Swift
override public func preferredStatusBarStyle() -> UIStatusBarStyle {
return .LightContent
}
override func prefersStatusBarHidden() -> Bool {
return false
}
No Objetivo-C
-(UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleLightContent; // your own style
}
- (BOOL)prefersStatusBarHidden {
return NO; // your own visibility code
}
Finalmente, verifique se o plist aplicativo que não têm a "visão baseada em controlador de barra de status aparência" conjunto de NO. Exclua essa linha ou defina-a como SIM (que acredito ser o padrão agora para o iOS 7?)
return self.topViewController;
funciona para mim, mas return self.visibleViewController;
- não
super
esse método e realmente deseja alterar o comportamento de todos os controladores desse tipo
Para quem ainda está lutando com isso, esta simples extensão rápida deve resolver o problema para você.
extension UINavigationController {
override open var childForStatusBarStyle: UIViewController? {
return self.topViewController
}
}
Meu aplicativo utilizado todos os três: UINavigationController
, UISplitViewController
, UITabBarController
,, assim, todos estes parecem assumir o controle sobre a barra de status e fará com que preferedStatusBarStyle
a não ser chamado para os seus filhos. Para substituir esse comportamento, você pode criar uma extensão como as demais respostas mencionadas. Aqui está uma extensão para todos os três, no Swift 4. A Wish Apple foi mais clara sobre esse tipo de coisa.
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: Atualização para mudanças na API do 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
}
}
A resposta da Tyson está correta ao alterar a cor da barra de status para branco UINavigationController
.
Se alguém quiser obter o mesmo resultado escrevendo o código AppDelegate
, use o código abaixo e escreva-o dentro do AppDelegate's
didFinishLaunchingWithOptions
método
E não se esqueça de configurá UIViewControllerBasedStatusBarAppearance
-lo YES
no arquivo .plist, caso contrário a alteração não será refletida.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// status bar appearance code
[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];
return YES;
}
Em um UINavigationController, preferredStatusBarStyle
não é chamado porque topViewController
é preferível self
. Portanto, para ser preferredStatusBarStyle
chamado em um UINavigationController, você precisa alterá-lo childViewControllerForStatusBarStyle
.
Substitua seu UINavigationController na sua classe:
class MyRootNavigationController: UINavigationController {
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
override var childViewControllerForStatusBarStyle: UIViewController? {
return nil
}
}
Para fazer isso em todos os UINavigationController, você pode substituir em uma extensão (aviso: afeta UIDocumentPickerViewController, UIImagePickerController, etc.), mas provavelmente não deve fazê-lo de acordo com a documentação do Swift :
extension UINavigationController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
open override var childViewControllerForStatusBarStyle: UIViewController? {
return nil
}
}
Além da resposta de serenn, se você estiver apresentando um controlador de exibição com um modalPresentationStyle
(por exemplo .overCurrentContext
), também deve chamar isso no novo controlador de exibição:
presentedViewController.modalPresentationCapturesStatusBarAppearance = true
Não se esqueça de substituir também o preferredStatusBarStyle
controlador de exibição apresentado.
Uma adição à resposta do Hippo: se você estiver usando um UINavigationController, provavelmente será melhor adicionar uma categoria:
// 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
Essa solução é provavelmente melhor do que mudar para o comportamento que será depreciado em breve.
preferredStatusBarStyle
e executa a lógica específica do UINavigationController. No momento, essa lógica é baseada, navigationBar.barStyle
mas eu posso ver verificações adicionais sendo adicionadas (por exemplo, UISearchDisplayController
movendo para ocultar o modo barra de navegação). Ao substituir a lógica padrão, você perde toda essa funcionalidade e se abre para momentos irritantes de 'wtf' no futuro. Veja minha resposta acima para a maneira correta de fazer isso, enquanto ainda suporta o comportamento interno do controlador de navegação.
Conforme mencionado na resposta selecionada , a causa raiz é verificar o objeto do controlador de visualização raiz da janela.
childForStatusBarStyle
Use as seguintes extensões, ele lida com todos os cenários acima -
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
}
}
UIViewControllerBasedStatusBarAppearance
digitar info.plist
como verdadeiro por padrãoCaso você apresente um novo fluxo de maneira modal, ele se desconecta do fluxo de estilo da barra de status existente. Portanto, suponha que você esteja apresentando um NewFlowUIViewController
e, em seguida, inclua um novo controlador de navegação ou tabBar e NewFlowUIViewController
, em seguida, adicione extensão de NewFlowUIViewController
para gerenciar ainda mais o estilo da barra de status do controlador.
Caso você defina modalPresentationStyle que fullScreen
não seja a apresentação modal, defina modalPresentationCapturesStatusBarAppearance
como true para que o controlador de exibição apresentado receba o controle de aparência da barra de status.
UINavigationController
é uma subclasse de UIViewController
(quem sabia 🙃)!
Portanto, ao apresentar controladores de exibição incorporados nos controladores de navegação, você realmente não está apresentando os controladores de exibição incorporados; você está apresentando os controladores de navegação! UINavigationController
, como uma subclasse de UIViewController
, herda preferredStatusBarStyle
e childForStatusBarStyle
, que você pode definir como desejado.
Qualquer um dos seguintes métodos deve funcionar:
info.plist
, adicione a seguinte propriedade:
UIUserInterfaceStyle
(também conhecida como "Estilo da interface do usuário")Substituir preferredStatusBarStyle
dentroUINavigationController
preferredStatusBarStyle
( doc ) - O estilo preferido da barra de status para o controlador de exibiçãoSubclasse ou extensão UINavigationController
class MyNavigationController: UINavigationController {
override var preferredStatusBarStyle: UIStatusBarStyle {
.lightContent
}
}
OU
extension UINavigationController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
.lightContent
}
}
Substituir childForStatusBarStyle
dentroUINavigationController
childForStatusBarStyle
( doc ) - Chamado quando o sistema precisa que o controlador de exibição seja usado para determinar o estilo da barra de status"Se o seu controlador de exibição de contêiner deriva seu estilo de barra de status de um de seus controladores de exibição filho, [substitua essa propriedade] e retorne esse controlador de exibição filho. Se você retornar nulo ou não substituir esse método, o estilo da barra de status será usado. Se o valor de retorno desse método for alterado, chame o método setNeedsStatusBarAppearanceUpdate (). "
Subclasse ou extensão UINavigationController
class MyNavigationController: UINavigationController {
override var childForStatusBarStyle: UIViewController? {
topViewController
}
}
OU
extension UINavigationController {
open override var childForStatusBarStyle: UIViewController? {
topViewController
}
}
Você pode retornar qualquer controlador de exibição que desejar acima. Eu recomendo um dos seguintes:
topViewController
(of UINavigationController
) ( doc ) - O controlador de exibição na parte superior da pilha de navegaçãovisibleViewController
(of UINavigationController
) ( doc ) - O controlador de visualização associado à visualização atualmente visível na interface de navegação (dica: isso pode incluir "um controlador de visualização que foi apresentado modalmente em cima do próprio controlador de navegação")Nota: Se você decidir fazer uma subclasse UINavigationController
, lembre-se de aplicar essa classe aos controladores de navegação por meio do inspetor de identidade no IB.
PS Meu código usa a sintaxe do Swift 5.1 😎
A resposta de @ serenn acima ainda é excelente para o caso de UINavigationControllers. No entanto, no swift 3, as funções childViewController foram alteradas para vars
. Portanto, o UINavigationController
código de extensão deve ser:
override open var childViewControllerForStatusBarStyle: UIViewController? {
return topViewController
}
override open var childViewControllerForStatusBarHidden: UIViewController? {
return topViewController
}
E então no controlador de exibição que deve ditar o estilo da barra de status:
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
Se o seu viewController estiver em UINavigationController.
Subclasse UINavigationController e adicione
override var preferredStatusBarStyle: UIStatusBarStyle {
return topViewController?.preferredStatusBarStyle ?? .default
}
O ViewController preferredStatusBarStyle
será chamado.
UIStatusBarStyle no iOS 7
A barra de status no iOS 7 é transparente, a exibição por trás disso é mostrada.
O estilo da barra de status refere-se às aparências de seu conteúdo. No iOS 7, o conteúdo da barra de status é escuro ( UIStatusBarStyleDefault
) ou claro ( UIStatusBarStyleLightContent
). Ambos UIStatusBarStyleBlackTranslucent
e UIStatusBarStyleBlackOpaque
estão obsoletos no iOS 7.0. Use em UIStatusBarStyleLightContent
vez disso.
Como mudar UIStatusBarStyle
Se abaixo da barra de status houver uma barra de navegação, o estilo da barra de status será ajustado para corresponder ao estilo da barra de navegação ( UINavigationBar.barStyle
):
Especificamente, se o estilo da barra de navegação for UIBarStyleDefault, o estilo da barra de status será UIStatusBarStyleDefault
; se o estilo da barra de navegação for UIBarStyleBlack
, o estilo da barra de status será UIStatusBarStyleLightContent
.
Se não houver barra de navegação abaixo da barra de status, o estilo da barra de status poderá ser controlado e alterado por um controlador de exibição individual enquanto o aplicativo é executado.
- [UIViewController preferredStatusBarStyle]
é um novo método adicionado no iOS 7. Pode ser substituído para retornar o estilo preferido da barra de status:
- (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent;
}
Se o estilo da barra de status deve ser controlado por um controlador de exibição filho em vez de por si próprio, substitua -[UIViewController childViewControllerForStatusBarStyle]
para retornar esse controlador de exibição filho.
Se você preferir desativar esse comportamento e definir o estilo da barra de status usando o -[UIApplication statusBarStyle]
método, adicione a UIViewControllerBasedStatusBarAppearance
chave ao Info.plist
arquivo de um aplicativo e atribua o valor NÃO.
Se alguém estiver usando um Controlador de Navegação e desejar que todos os controladores de navegação tenham o estilo preto, você poderá escrever uma extensão para o UINavigationController como este no Swift 3 e ela será aplicada a todos os controladores de navegação (em vez de atribuí-lo a um controlador em um Tempo).
extension UINavigationController {
override open func viewDidLoad() {
super.viewDidLoad()
self.navigationBar.barStyle = UIBarStyle.black
}
}
No Swift para qualquer tipo de UIViewController:
No seu AppDelegate
conjunto:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window!.rootViewController = myRootController
return true
}
myRootController
pode ser qualquer tipo de UIViewController
, por exemplo, UITabBarController
ou UINavigationController
.
Em seguida, substitua esse controlador raiz assim:
class RootController: UIViewController {
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return .LightContent
}
}
Isso mudará a aparência da barra de status em todo o aplicativo, porque o controlador raiz é o único responsável pela aparência da barra de status.
Lembre-se de definir a propriedade View controller-based status bar appearance
como YES no seu Info.plist
para fazer esse trabalho (que é o padrão).
A maioria das respostas não inclui uma boa implementação de childViewControllerForStatusBarStyle
método para UINavigationController
. De acordo com minha experiência, você deve lidar com casos como quando o controlador de exibição transparente é apresentado sobre o controlador de navegação. Nesses casos, você deve passar o controle para o seu controlador modal ( visibleViewController
), mas não quando estiver desaparecendo.
override var childViewControllerForStatusBarStyle: UIViewController? {
var childViewController = visibleViewController
if let controller = childViewController, controller.isBeingDismissed {
childViewController = topViewController
}
return childViewController?.childViewControllerForStatusBarStyle ?? childViewController
}
Quanto ao iOS 13.4, o preferredStatusBarStyle
método na UINavigationController
categoria não será chamado, o swizzling parece ser a única opção sem a necessidade de usar uma subclasse.
Exemplo:
Cabeçalho da categoria:
@interface UINavigationController (StatusBarStyle)
+ (void)setUseLightStatusBarStyle;
@end
Implementação:
#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 no AppDelegate.h:
#import "UINavigationController+StatusBarStyle.h"
[UINavigationController setUseLightStatusBarStyle];
Aqui está o meu método para resolver isso.
Defina um protocolo chamado AGViewControllerAppearance .
AGViewControllerAppearance.h
#import <Foundation/Foundation.h>
@protocol AGViewControllerAppearance <NSObject>
@optional
- (BOOL)showsStatusBar;
- (BOOL)animatesStatusBarVisibility;
- (UIStatusBarStyle)preferredStatusBarStyle;
- (UIStatusBarAnimation)prefferedStatusBarAnimation;
@end
Defina uma categoria no UIViewController chamada 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
Agora, é hora de dizer que você está vendo o controlador implementando o protocolo AGViewControllerAppearance .
Exemplo:
@interface XYSampleViewController () <AGViewControllerAppearance>
... the rest of the interface
@end
Obviamente, você pode implementar o restante dos métodos ( showsStatusBar , animatesStatusBarVisibility , prefferedStatusBarAnimation ) a partir do protocolo e o UIViewController + Upgrade fará a personalização adequada com base nos valores fornecidos por eles.
Observe que ao usar a self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
solução
não deixe de ir ao seu pedido e defina "Exibir aparência da barra de status com base no controlador" como YES. Se não, não vai funcionar.
Desde o Xcode 11.4, a substituição da preferredStatusBarStyle
propriedade em uma extensão UINavigationController não funciona mais, pois não será chamada.
Definir a barStyle
de navigationBar
que .black
obras de fato, mas isso vai adicionar efeitos colaterais indesejados se você adicionar subviews à NavigationBar que pode ter aparências diferentes para o modo claro e escuro. Como a configuração barStyle
para preto, a userInterfaceStyle
visualização incorporada na barra de navegação sempre terá, userInterfaceStyle.dark
independentemente do userInterfaceStyle
aplicativo.
A solução adequada que eu encontro é adicionar uma subclasse UINavigationController
e substituir preferredStatusBarStyle
lá. Se você usar esse UINavigationController personalizado para todas as suas visualizações, estará no lado de salvar.
O NavigationController ou o TabBarController são os que precisam fornecer o estilo. Aqui está como eu resolvi: https://stackoverflow.com/a/39072526/242769