UIStatusBarStyle PreferredStatusBarStyle não funciona no iOS 7

110

Na minha aplicação do iPhone construído com Xcode 5 para iOS 7 I set UIViewControllerBasedStatusBarAppearance=YESem info.plist, e na minhaViewController Eu tenho esse código:

-(UIStatusBarStyle) preferredStatusBarStyle
{
    return UIStatusBarStyleLightContent;
}

Mas a barra de status ainda está preta contra o fundo preto.

Eu sei que é possível mudar-wide aplicativo, fixando UIViewControllerBasedStatusBarAppearance=NOem info.plist, mas eu realmente preciso de alterar isso em um viewControllerpor viewControllerbase em tempo de execução.

Andrew Smith
fonte
Olá, tenho o mesmo problema que você mencionou. Você conseguiu a solução? Por favor, me forneça isso.
Noundla Sandeep
Você pode dar uma olhada em: Alterar a cor do texto da barra de status dos aplicativos
1218GG

Respostas:

281

Eu descobri que se o seu ViewController está dentro de um navigationController, o navigationController navigationBar.barStyledetermina o statusBarStyle.

Definir a barra de navegação barStylepara UIBarStyleBlackTranslucentfornecerá o texto da barra de status em branco (ou seja. UIStatusBarStyleLightContent), E UIBarStyleDefaultfornecerá o texto da barra de status em preto (ou seja,UIStatusBarStyleDefault ).

Observe que isso se aplica mesmo se você alterar totalmente a cor da barra de navegação por meio dela barTintColor.

mxcl
fonte
isso faz sentido para mim ... ótimo
Nick,
14
Acredito que seja porque o UINavigationController's preferredStatusBarStylenão chama o ViewController que hospeda e, em vez disso, apenas retorna com base em seu navigationBarStyle.
mxcl
Nesse caso, a visualização não está dentro de um controlador de navegação.
Andrew Smith,
Muito contra-intuitivo pensar que o estilo de barra tem preferência sobre um método implementado no controlador de visualização, e apenas ao apresentar visualizações modais!
Matej
3
UIBarStyleBlackTranslucent está obsoleto, use em UIBarStyleBlackvez disso
Nhon Nguyen
87

OK, aqui está o truque. Você precisa adicionar a chave "Exibir barra de status baseada no controlador" e definir o valor como No.

Isso é contrário ao que parece ser o significado desta chave, mas mesmo se você definir o valor para No, ainda poderá alterar a aparência da barra de status e se ela é exibida ou não em qualquer controlador de visualização. Portanto, ele age como "Sim", mas defina como "Não"!

Agora posso obter a barra de status branca ou escura.

Andrew Smith
fonte
6
Para mim, isso estava errado. A chave precisava ser definida como "Sim", como seria de esperar. Estou no Xcode 5.1 iOS 7.1, então talvez tenha mudado.
joel.d
Estou usando o Xcode 5.1 e iOS 7.1 também e NÃO funcionou para mim ... ESTRANHO.
Arjun Mehta
Onde devo adicionar esta chave?
Hadu
Em seu arquivo [AppName] -Info.plist
Saren Inden
1
Funciona bem quando a tecla "View controller-based status bar" está definida como "YES" com Xcode6.0, iOS 8.0
bpolat
77

Para preferredStatusBarStyle()trabalhar com UINavigationControllere UITabBarControlleradiciono o código a seguir, que obterá o estilo de barra de status preferido do controlador de visualização atualmente visível.

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

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

Para o Swift 3, esses não são métodos, mas propriedades:

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

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

As propriedades do Swift 4.2 foram renomeadas:

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

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

Uso

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
fonte
6
Esta é de longe a melhor resposta (para aplicativos que usam UINavigationController ou UITabBarController
Kesava
1
qual é o uso para isso?
AnBisw
@Annjawn esses métodos são usados ​​pelo UIKit. Você não precisa fazer nada além de adicioná-lo ao seu projeto.
Daniel Wood
@DanielWood yeah, descobri isso e esqueci completamente que usei exatamente a mesma coisa em um dos meus projetos anteriores, embora de forma um pouco diferente.
AnBisw
Esta é de fato a melhor resposta
Musa almatri
33

Posso estar chegando lá um pouco tarde, mas caso alguém esteja procurando por uma solução funcional e verificada para o aplicativo.

@mxcl está correto ao descrever por que isso está acontecendo. Para corrigir isso, simplesmente criamos uma extensão (ou categoria em obj-c) que substitui o método preferredSatusBarStyle () de UINavigationController. Aqui está um exemplo em Swift:

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

Este código simplesmente extrai o primeiro controlador de visão (o controlador de visão raiz) e o desembrulha (em obj-c, verifique se ele não é nulo). Se o desempacotamento for bem-sucedido (não nulo), pegamos o rootViewControllers preferredStatusBarStyle. Caso contrário, apenas retornamos o padrão.

Espero que isso ajude alguém que possa precisar.

Kyle Begeman
fonte
2
No Swift 2.0, você deve remover "as? UIViewController" da instrução de condição.
Thomás Calmon de
Brilhante, fiz uma modificação além de remover a instrução "como", mudei de "primeiro" para "último" desta forma, qualquer controlador de visualização que esteja sendo visto pelo usuário no topo da pilha terá a capacidade de controlar a cor da barra de status. Ótimo trabalho, obrigado por compartilhar!
Unome
1
Se o seu controlador de navegação não tiver controladores de visualização, isso causaria um loop infinito. return self.preferredStatusBarStyle()chamaria de volta neste mesmo método.
bearMountain
1
No meu caso, em vez de usar rootViewController, usei topViewController, pois durante a pilha o estilo pode mudar.
Ric Santos
2
@Unome visibleViewControllerseria ainda melhor
Cœur
21

Para fornecer mais detalhes sobre a resposta aceita, coloque a seguinte linha no didFinishLaunchingWithOptions:método do delegado do aplicativo :

[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;

Em seguida, em seu Info.plist, adicione View controller-based status bar appearancee defina como NO.

Eu acredito que é assim que deve ser feito, NÃO a partir do controlador de navegação, se você quiser a mesma cor da barra de status para todo o aplicativo. Você pode ter telas que não estão necessariamente embutidas em uma UINavigationController, ou em uma UINavigationControllersubclasse diferente em outro lugar, e outras coisas.

EDIT : Você também pode fazer isso sem digitar nenhum código: https://stackoverflow.com/a/18732865/855680

Matthew Quiros
fonte
1
Observe que esta forma está obsoleta do IOS 9.0
Mohamed Salah
10

Em viewDidLoad basta escrever isso

[self setNeedsStatusBarAppearanceUpdate];

apenas faça isso e vai funcionar

você pode tentar isso?

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

Mais uma coisa que vi em sua pergunta, que você escreveu o método como este

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

mas deveria ser assim

-(UIStatusBarStyle)preferredStatusBarStyle{ 
    return UIStatusBarStyleLightContent; 
} 
Usuário 1531343
fonte
Isso faz com que o método preferredStatusBarStyle seja chamado, mas ainda assim a barra de status fica preta.
Andrew Smith,
consulte minha resposta atualizada ... diga-me rapidamente se funciona ou não
Usuário 1531343,
Minha pergunta original diz explicitamente que preciso fazer o controle de visualização por visualização da barra de status.
Andrew Smith,
Você pode verificar seu código com referência à minha pergunta atualizada?
Usuário 1531343,
1
[self setNeedsStatusBarAppearanceUpdate];um método tão bom, obrigado!
Supertecnoboff
6

Aqui está como eu resolvi isso. Normalmente o navigationController ou tabBarController são os que decidem a aparência da barra de status (oculto, cor, etc).

Então, acabei criando uma subclasse do controlador de navegação e substituindo preferredStatusBarStyle. se o atual ViewContorller visível implementa StatusBarStyleHandler, eu peço que o valor seja usado como o estilo, se não, eu apenas retorno um valor padrão.

A maneira como você dispara uma atualização da aparência da barra de status é chamando setNeedsStatusBarAppearanceUpdatequal dispara preferredStatusBarStylenovamente e atualiza a IU de acordo com o que o método retorna

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

Então uso

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
fonte
Esta postagem seria muito melhor se você pudesse explicar por que e como isso corrige o problema.
Em vez de criar uma subclasse de UINavigationController, você também pode apenas criar uma extensão para UINavigationController e obter o mesmo resultado sem precisar criar uma subclasse.
Wilforeal
4

1) Uma configuração para todo o projeto:

Se disponível, remova o UIViewControllerBasedStatusBarAppearancepar de valores-chave de seu info.plist ou defina NOsem removê-lo. Se não estiver disponível em seu info.plist, não faça nada. O padrão é NOpara esta propriedade.

Adicione o código abaixo ao seu AppDelegate.m:

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

2) Configurações diferentes para controladores de visualização diferentes:

Adicione UIViewControllerBasedStatusBarAppearanceum par de valores-chave ao seu info.plist e defina-o como YES.

Se o seu View Controller não estiver incorporado ao Navigation Controller. Digamos MyViewController. basta adicionar o código abaixo ao seu arquivo MyViewController.m. Se o seu View Controller estiver embutido no Navigation Controller, crie uma nova Cocoa Touch Class e torne-a uma subclasse de UINavigationController. Digamos MyNC. Selecione Navigation Controller View em seu Storyboard, no painel direito; Utilitários -> Inspetor de identidade -> Classe personalizada -> Classe, digite "MyNC". Depois de vincular o Storyboard View à sua classe Cocoa Touch "MyNC", adicione o código abaixo ao seu MyNC.m:

- (BOOL)prefersStatusBarHidden {
    return NO;
}

-(UIStatusBarStyle)preferredStatusBarStyle {
    return UIStatusBarStyleLightContent;
}
Fatih Aksu
fonte
Parece que no iOS9 UIViewControllerBasedStatusBarAppearance, por padrão, contém o valor SIM, pois precisei adicioná-lo manualmente em .plist e definir como NÃO para funcionar corretamente.
Mohd Asim
4

Mesmo com todas as respostas aqui ainda não achei a solução exata para mim, mas comecei com a resposta do Daniel. O que eu terminei foi:

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

em controladores de navegação (semelhante para tab, apenas selectedViewController). E então respeitará:

override var preferredStatusBarStyle: UIStatusBarStyle {
     return .lightContent
}

Em cada controlador de visualização, a menos que você defina de outra forma. Eu não preciso ligar para setNeedsStatusBarAppearanceUpdate()lugar nenhum, ele apenas atualiza quando você chega em cada controlador de visualização.

Andrew Plummer
fonte
2
Acabei com a solução quase idêntica depois de lutar com isso por horas.
Scott Jungwirth
Em algum ponto isso parece ter sido corrigido, apenas usar preferredStatusBarStyle em cada VC funciona bem para mim agora.
Andrew Plummer,
4

Solução (ões) iOS 13

A resposta com maior votação usa código "legado" 👎

A configuração da barStylepropriedade agora (iOS 13+) é considerada uma "personalização legada". De acordo com a Apple ,

No iOS 13 e posterior, personalize sua barra de navegação usando as propriedades standardAppearance, compactAppearance e scrollEdgeAppearance. Você pode continuar a usar esses acessores legados para personalizar a aparência da sua barra de navegação diretamente, mas você mesmo deve atualizar a aparência para diferentes configurações de barra.

Em relação à sua tentativa - Você estava no caminho certo!

UINavigationControlleré uma subclasse de UIViewController(quem diria 🙃)!

Portanto, ao apresentar controladores de visualização embutidos em controladores de navegação, você não está realmente apresentando os controladores de visualização embutidos; você está apresentando os controladores de navegação! UINavigationController, como uma subclasse de UIViewController, herda preferredStatusBarStylee childForStatusBarStyle, que você pode definir conforme desejado.

Qualquer um dos seguintes métodos deve funcionar:

  1. Substituir preferredStatusBarStyleemUINavigationController

    • preferredStatusBarStyle( doc ) - O estilo de barra de status preferido para o controlador de visualização
    • Subclasse ou extensão UINavigationController

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

      OU

      extension UINavigationController {
          open override var preferredStatusBarStyle: UIStatusBarStyle {
              .lightContent
          }
      }
  2. Substituir childForStatusBarStyleemUINavigationController

    • childForStatusBarStyle( doc ) - Chamado quando o sistema precisa do controlador de visualização para usar para determinar o estilo da barra de status
    • De acordo com a documentação da Apple,

      "Se o seu controlador de visualização de contêiner deriva seu estilo de barra de status de um de seus controladores de visualização filho, [substitua esta propriedade] e retorne aquele controlador de visualização filho. Se você retornar nulo ou não substituir este método, o estilo de barra de status para self é usado . Se o valor de retorno desse método mudar, chame o método setNeedsStatusBarAppearanceUpdate (). "

    • Em outras palavras, se você não implementar a solução 3 aqui, o sistema voltará para a solução 2 acima.
    • 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 visualização que desejar acima. Eu recomendo um dos seguintes:

      • topViewController(of UINavigationController) ( doc ) - O controlador de visualização no topo da pilha de navegação
      • visibleViewController(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 criar uma subclasse UINavigationController, lembre-se de aplicar essa classe aos seus controladores de navegação por meio do inspetor de identidade no IB.

PS Meu código usa a sintaxe Swift 5.1 😎

Andrew Kirna
fonte
1
Resposta muito completa, obrigado! Além disso, algo que lutei por um tempo, no iOS 13 o .defaultestilo leva em consideração o modo escuro e não é documentado, então se você também oferece suporte a versões anteriores do iOS, pode adicionar if #available(iOS 13, *) { return .darkContent } else { return .default }se estiver tentando configurar o estilo da barra de status manualmente de acordo com o atrás da barra de status e essa cor é "brilhante".
valcanaia
1
Observe que o método de extensão para fazer a substituição de var não funciona mais no Xcode 11.4 / iOS 13.4
Marc Etcheverry
Porque a extensão de classes C objetivo em Swift é implementada por meio de categorias C Objective. Substituir métodos por meio de categorias Objective C não é recomendado e provavelmente falhará. Consulte stackoverflow.com/a/38274660/2438634
Marc Etcheverry
Embora a substituição do UINavigationController definitivamente funcione, parece um bug do lado da Apple que não faz o childForStatusBarStyle retornar por padrão seu topViewController. Por exemplo, UITabBarController faz isso para suas guias. Para mim, não há nenhuma razão para que UINavigationController, sendo um controlador estritamente de contêiner para hospedar controladores "reais" de Visualização em vez de apresentar sua própria IU, deva "engolir" esses estilos de barra de status. Vai criar um radar para a Apple.
Igor Vasilev
1

Se você quiser ocultar o statusBar durante splashScreen, mas quiser alterar o estilo para conteúdo claro (StatusBarInitiallyHidden no Plist deve ser NÃO para ocultar o statusBar no splash), você pode adicionar isso ao método didFinishLaunchingWithOptions do appDelegate para alterar para lightContent.

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

exemplo rápido

em AppDelegate.swift

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

    return true
}

in info.plist set Ver aparência da barra de status baseada no controlador: NÃO

Fyalavuz
fonte
1

Se você estiver usando NavigationController, você pode criar NavigationControlleruma subclasse para que consulte seu controlador de visualização filho

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

Swift 4.2

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

extension UINavigationController {
    open override var childForStatusBarStyle: UIViewController? {
        return visibleViewController
    }
}
Vyacheslav
fonte
Observe que o método de extensão de substituir var não funciona mais no Xcode 11.4 / iOS 13.4
Marc Etcheverry
@MarcEtcheverry então, por que você negou a resposta? parece estranho.
Vyacheslav
Porque a extensão de classes C objetivo em Swift é implementada por meio de categorias C Objective. Substituir métodos por meio de categorias Objective C não é recomendado e provavelmente falhará. Consulte stackoverflow.com/a/38274660/2438634
Marc Etcheverry
@MarcEtcheverry 'não recomendado'! = 'Nunca use!'. para julho de 1818 a resposta estava correta. Mesmo esta resposta não está atualizada, não é um motivo para rejeitá-la. Não consigo ver o futuro
Vyacheslav
0

Você pode definir o estilo da barra de status. Ele se parecerá com a barra de status como IOS 6 e abaixo.
Cole estes métodos em seu controlador de visualização

-(UIStatusBarStyle)preferredStatusBarStyle{
    return UIStatusBarStyleBlackOpaque;
}

e chamar este método a partir da visualização carregou assim

if([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f)
    {
       [self setNeedsStatusBarAppearanceUpdate];
    }
Ganapatia
fonte
Você quer dizer [self setStatusBarNeedsUpdate]no segundo bloco? (Ou algo mais, pelo menos).
mxcl
0

Só quero adicionar uma nota para um caso específico que enfrentei. Eu tinha outra UIWindow em meu aplicativo para exibir um chat flutuando em todo o meu aplicativo o tempo todo. Fazer isso não fez com que nenhuma das soluções acima funcionasse, e não tenho certeza do motivo! Tudo o que notei é que meu ViewController na nova UIWindow era a razão para isso! E se eu quiser mudar o estilo da barra de status, tenho que fazer isso naquele controlador de visualização da nova UIWindow.

Esta nota pode ajudar outras pessoas que tenham uma estrutura semelhante! Basicamente, você pode aplicar as soluções mencionadas acima no ViewController da nova UIWindow.

Novamente, este é um caso específico.

obrigado

Ehab Saifan
fonte
-1

Para swift 3, em seu UIViewController:

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