selectedStatusBarStyle não é chamado

257

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.)

trgoofi
fonte
Não é chamado em que contexto: simulador? em um dispositivo?
bneely
@bneely os dois.
trgoofi
Você está usando o simulador do iOS 7, um dispositivo iOS 7 e seu SDK básico é 7.0?
bneely
@bneely O iOS SDK 7.0 é exibido abaixo do nome do meu projeto, isso significa que meu SDK base é 7.0?
trgoofi
Nas configurações de compilação, "SDK Base" é onde o valor está definido. Parece que seu projeto está definido como 7.0.
bneely

Respostas:

117

Possível causa raiz

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 UIViewControllerem que eu havia implementado o preferredStatusBarStylefoi 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 preferredStatusBarStylemé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;
}

Método alternativo (descontinuado no iOS 9)

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 UIViewControllerBasedStatusBarAppearancepara NOno arquivo plist se você usar este método.

AbdullahC
fonte
2
Eu tenho o mesmo problema que você, não configurando o controlador de exibição raiz. Como diabos você achou isso?
trgoofi
1
Suspeitei que algo na estrutura não estava recebendo a notificação setNeedsStatusBarAppearanceUpdate- minhas suspeitas foram confirmadas quando fiz essa alteração.
AbdullahC
2
Um problema relacionado que encontrei em um aplicativo foi um controlador de exibição com um controlador de exibição filho em tela cheia que não substituiu childViewControllerForStatusBarStyle e childViewControllerForStatusBarHidden para retornar esse controlador de exibição filho. Se você possui sua própria hierarquia de controlador de visualização, precisará fornecer esses métodos para informar ao sistema qual controlador de visualização deve ser usado para determinar o estilo da barra de status.
21413 Jon Steinmetz
definir o rootviewcontroller não muda nada. Você deve trabalhar com o comentário de Jon. E tenha cuidado ao chamar setneedsstatusbarappearanceUpdate. Você deve chamá-lo dos pais para trabalhar.
precisa saber é o seguinte
1
@ Hippo você é gênio !! Como você descobriu que era por não definir o rootviewcontroller?
ViruMax 7/11
1019

Para quem usa um UINavigationController:

O UINavigationControllernão encaminha as preferredStatusBarStylechamadas 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 preferredStatusBarStyleem seus VCs dentro de um controlador nav não fará nada - eles nunca serão chamados.

O truque é quais são os UINavigationControllerusos para decidir para o que retornar UIStatusBarStyleDefaultou UIStatusBarStyleLightContent. Ele baseia isso no seu UINavigationBar.barStyle. O padrão ( UIBarStyleDefault) resulta na UIStatusBarStyleDefaultbarra de status escura do primeiro plano . E UIBarStyleBlackdará uma UIStatusBarStyleLightContentbarra de status.

TL; DR:

Se você quiser UIStatusBarStyleLightContentem um UINavigationControlleruso:

self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
Tyson
fonte
59
Agradável! Observe que preferredStatusBarStyle, de fato, será chamado no controlador de exibição filho se você ocultar a barra de navegação (definida navigationBarHiddencomo YES), exatamente como apropriado.
Patrick Pijnappel
25
Obrigado por esta resposta. Se você deseja definir o barStyle para todas as suas barras de navegação, chamada[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack]
Thomas Desert
15
Resposta perfeita. Nenhuma das outras respostas no SO levou o UINavigationController em consideração. 2 horas batendo minha cabeça no teclado.
Ryan Alford
10
Parabéns ao @Patrick por indicar que o navigationBarHiddenconjunto como YESrealmente preferredStatusBarStyleligou e um aviso para aqueles que podem tropeçar nisso: funciona com navigationBarHidden, mas não com navigationBar.hidden!
jcaron
4
deve ser óbvio, mas você também precisa de "Exibir aparência da barra de status baseada no controlador" definida como YES no Info.plist para que isso funcione.
Code Baller
99

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?)

serenn
fonte
Looks como return self.topViewController;funciona para mim, mas return self.visibleViewController;- não
k06a
visibleViewController pode retornar o controlador modal atualmente apresentado quando você o dispensar. O que é chato. Use topViewController.
Ben Sinclair
1
@ d.lebedev ok, mas acho que nenhum desses problemas se aplica aqui. Você não precisa chamar superesse método e realmente deseja alterar o comportamento de todos os controladores desse tipo
ed '
1
isso não está funcionando para mim no iOS 9.3. Acho que esse é o problema: esse problema é de particular importância porque muitas das classes de cacau são implementadas usando categorias. Um método definido pela estrutura que você tenta substituir pode ter sido implementado em uma categoria e, portanto, qual implementação tem precedência não está definida.
vikingosegundo
2
Isso está errado e quebra no iOS 13.4. Como a extensão de classes C objetivas no Swift é implementada por meio das categorias Objective C. A substituição de métodos pelas categorias do Objetivo C não é recomendada e provavelmente quebrará. Veja stackoverflow.com/a/38274660/2438634
Marc Etcheverry
79

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
    }
}
Alex Brown
fonte
10
Você merece uma medalha.
Nikans 29/07
2
Muito obrigado cara. Eu estava retornando visibleViewController sem sucesso.
Fábio Salata 23/08
1
Isto é ouro. Eu tenho um controlador de navegação incorporado em uma barra de guias e joguei isso em um arquivo e agora posso alterar a aparência da barra de status para onde quiser.
Vahid Amiri 23/05
2
Isso está errado e quebra no iOS 13.4. Como a extensão das classes de objetivo C no Swift é implementada por meio das categorias de objetivo C. A substituição de métodos pelas categorias do Objetivo C não é recomendada e provavelmente quebrará. Veja stackoverflow.com/a/38274660/2438634
Marc Etcheverry
1
@ MarcEtcheverry esta instância em particular não estava errada. O fato é que as subclasses de outros objetos / protocolos, como o UINavigationController, não tinham uma implementação prévia para entrar em conflito no envio dinâmico. Não houve padrões ou implementações nas subclasses reais, motivo pelo qual essa foi a maneira mais limpa de implementar isso em um aplicativo sem criar uma dependência desnecessária (ponto final). Infelizmente, 13.4 parece ter alterado esse comportamento. Acho que nos bastidores eles têm uma verificação ou implementação agora que não existe há anos .........
TheCodingArt
20

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 preferedStatusBarStylea 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
    }
}
Luis
fonte
1
Esta é a única solução que funciona. Todas as respostas no SO apontam para a solução padrão que não funcionará em nenhum aplicativo com o NavigationControllers. Obrigado!!!
Houman 18/08/19
O uso de extensões para substituir está errado. Isso não é seguro. Existem várias soluções mais simples. Use uma subclasse.
Sulthan
2
Isso está errado e quebra no iOS 13.4. Como a extensão das classes de objetivo C no Swift é implementada por meio das categorias de objetivo C. A substituição de métodos pelas categorias do Objetivo C não é recomendada e provavelmente quebrará. Veja stackoverflow.com/a/38274660/2438634
Marc Etcheverry
1
@ MarcEtcheverry esta instância em particular não estava errada. O fato é que as subclasses de outros objetos / protocolos, como o UINavigationController, não tinham uma implementação prévia para entrar em conflito no envio dinâmico. Não houve padrões ou implementações nas subclasses reais, motivo pelo qual essa foi a maneira mais limpa de implementar isso em um aplicativo sem criar uma dependência desnecessária (ponto final). Infelizmente, 13.4 parece ter alterado esse comportamento. Acho que nos bastidores eles têm uma verificação ou implementação agora que não existe há anos .........
TheCodingArt
15

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 didFinishLaunchingWithOptionsmétodo

E não se esqueça de configurá UIViewControllerBasedStatusBarAppearance-lo YESno arquivo .plist, caso contrário a alteração não será refletida.

Código

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

     return YES;
}
Yogesh Suthar
fonte
14

Em um UINavigationController, preferredStatusBarStylenão é chamado porque topViewControlleré preferível self. Portanto, para ser preferredStatusBarStylechamado em um UINavigationController, você precisa alterá-lo childViewControllerForStatusBarStyle.

Recomendação

Substitua seu UINavigationController na sua classe:

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

Alternativa não recomendada

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
    }
}
Cœur
fonte
11

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 preferredStatusBarStylecontrolador de exibição apresentado.

frin
fonte
9

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.

Artem Abramov
fonte
Não faça isso, ele funciona por enquanto, mas pode interromper o comportamento futuro. Basta alterar o estilo navBar - veja minha resposta stackoverflow.com/a/19513714/505457
Tyson
2
Você deve usar subclasse, não categoria.
shuiyouren
2Tyson: Por que isso irá quebrar o comportamento futuro? preferidoStatusBarStyle: é o método preferido da Apple para configurar o estilo da Barra de Status.
Artem Abramov
2shuiyouren: Por que devo aumentar a complexidade subclassificando se posso apenas usar uma categoria e incluí-la em todos os lugares para onde devo? Enfim, isso é uma questão de arquitetura, não de implementação.
Artem Abramov
2
@ArtemAbramov Como o UINavigationController já implementa preferredStatusBarStylee executa a lógica específica do UINavigationController. No momento, essa lógica é baseada, navigationBar.barStylemas eu posso ver verificações adicionais sendo adicionadas (por exemplo, UISearchDisplayControllermovendo 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.
Tyson
9

Swift 4.2 e superior

Conforme mencionado na resposta selecionada , a causa raiz é verificar o objeto do controlador de visualização raiz da janela.

Casos possíveis da sua estrutura de fluxo

  • O objeto UIViewController personalizado

    é um controlador de visualização raiz da janela O seu controlador de visualização raiz da janela é um objeto UIViewController e adiciona ou remove ainda o controlador de navegação ou o tabController com base no fluxo do aplicativo.

    Esse tipo de fluxo geralmente é usado se o aplicativo tiver um fluxo de pré-login na pilha de navegação sem guias e postar o fluxo de login com guias e, possivelmente, todas as guias contenham o controlador de navegação.

  • O objeto TabBarController é o controlador de visualização raiz da janela

    Este é o fluxo em que o controlador de visualização raiz da janela é tabBarController, possivelmente todas as guias mantêm ainda o controlador de navegação.

  • O objeto NavigationController é o controlador de visualização raiz da janela

    Este é o fluxo em que o controlador de visualização raiz da janela é navigationController.

    Não tenho certeza se existe a possibilidade de adicionar um controlador de barra de guias ou um novo controlador de navegação em um controlador de navegação existente. Mas, se houver, precisamos passar o controle de estilo da barra de status para o próximo contêiner. Então, adicionei a mesma verificação na extensão UINavigationController para encontrarchildForStatusBarStyle

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
    }
}
  • Você não precisa UIViewControllerBasedStatusBarAppearancedigitar info.plistcomo verdadeiro por padrão

Pontos a considerar para fluxos mais complexos

  • Caso 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 NewFlowUIViewControllere, em seguida, inclua um novo controlador de navegação ou tabBar e NewFlowUIViewController, em seguida, adicione extensão de NewFlowUIViewControllerpara gerenciar ainda mais o estilo da barra de status do controlador.

  • Caso você defina modalPresentationStyle que fullScreennão seja a apresentação modal, defina modalPresentationCapturesStatusBarAppearancecomo true para que o controlador de exibição apresentado receba o controle de aparência da barra de status.

abhimanyu jindal
fonte
Excelente resposta!
Amin Benarieb
3
Isso está errado e quebra no iOS 13.4. Como a extensão das classes de objetivo C no Swift é implementada por meio das categorias de objetivo C. A substituição de métodos pelas categorias do Objetivo C não é recomendada e provavelmente quebrará. Veja stackoverflow.com/a/38274660/2438634
Marc Etcheverry
@ MarcEtcheverry esta instância em particular não estava errada. O fato é que as subclasses de outros objetos / protocolos, como o UINavigationController, não tinham uma implementação prévia para entrar em conflito no envio dinâmico. Não houve padrões ou implementações nas subclasses reais, motivo pelo qual essa foi a maneira mais limpa de implementar isso em um aplicativo sem criar uma dependência desnecessária (ponto final). Infelizmente, 13.4 parece ter alterado esse comportamento. Eu estou supondo nos bastidores que eles tenham uma verificação ou implementação agora que não existe há anos .........
TheCodingArt
8

iOS 13 Soluções

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 preferredStatusBarStylee childForStatusBarStyle, que você pode definir como desejado.

Qualquer um dos seguintes métodos deve funcionar:

  1. Desative totalmente o modo escuro
    • No seu info.plist, adicione a seguinte propriedade:
      • Chave - UIUserInterfaceStyle(também conhecida como "Estilo da interface do usuário")
      • Valor - Leve
  2. Substituir preferredStatusBarStyledentroUINavigationController

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

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

      OU

      extension UINavigationController {
          open override var preferredStatusBarStyle: UIStatusBarStyle {
              .lightContent
          }
      }
  3. Substituir childForStatusBarStyledentroUINavigationController

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

      "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 (). "

    • Em outras palavras, se você não implementar a solução 3 aqui, o sistema voltará à 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 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çã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 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 😎

Andrew Kirna
fonte
Minha barra de status fica preta após a rotação da tela. Alguma idéia do porquê? Isso acontece apenas no simulador do iPad Pro.
Pedro Paulo Amorim
@PedroPauloAmorim, você pode fornecer mais informações? Como é apresentado o controlador da vista superior (modal, tela cheia, programa)? Está aninhado dentro de um controlador de navegação? O texto está ficando preto ou o fundo também? O que você está tentando realizar?
Andrew Kirna 22/01
Defino a barra de status da luz em todo o aplicativo. Fica claro em duas rotações, no terceiro fica escuro e nunca mais volta, forçando-o a redesenhar. Está acontecendo no simulador do iPad Pro. As visualizações estão sendo apresentadas em tela cheia e não estão aninhadas dentro de um controlador de navegação. Somente o texto fica escuro.
Pedro Paulo Amorim
Como você está definindo a barra de status da luz em primeiro lugar?
Andrew Kirna 29/01
3
Sua substituição via extensão não é uma substituição real. É um mau uso inseguro do idioma. Isso pode quebrar com muita facilidade.
Sulthan
7

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 UINavigationControllercó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
}
John Stricker
fonte
2
Isso está errado e quebra no iOS 13.4. Como a extensão das classes de objetivo C no Swift é implementada por meio das categorias de objetivo C. A substituição de métodos pelas categorias do Objetivo C não é recomendada e provavelmente quebrará. Veja stackoverflow.com/a/38274660/2438634
Marc Etcheverry
@ MarcEtcheverry esta instância em particular não estava errada. O fato é que as subclasses de outros objetos / protocolos, como o UINavigationController, não tinham uma implementação prévia para entrar em conflito no envio dinâmico. Não houve padrões ou implementações nas subclasses reais, motivo pelo qual essa foi a maneira mais limpa de implementar isso em um aplicativo sem criar uma dependência desnecessária (ponto final). Infelizmente, 13.4 parece ter alterado esse comportamento. Eu estou supondo nos bastidores que eles tenham uma verificação ou implementação agora que não existe há anos .........
TheCodingArt
6

Se o seu viewController estiver em UINavigationController.

Subclasse UINavigationController e adicione

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

O ViewController preferredStatusBarStyleserá chamado.

PowHu
fonte
4

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 UIStatusBarStyleBlackTranslucente UIStatusBarStyleBlackOpaqueestão obsoletos no iOS 7.0. Use em UIStatusBarStyleLightContentvez 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 UIViewControllerBasedStatusBarAppearancechave ao Info.plistarquivo de um aplicativo e atribua o valor NÃO.

oscarr
fonte
3

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
    }

}
Benjamin Lowry
fonte
1
Mas e se a barra de navegação estiver oculta?
Slavcho 30/01
1
Porque preciso que a navegação esteja oculta e a barra de status esteja visível.
Slavcho 30/01
1

No Swift para qualquer tipo de UIViewController:

No seu AppDelegateconjunto:

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

myRootControllerpode ser qualquer tipo de UIViewController, por exemplo, UITabBarControllerou 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 appearancecomo YES no seu Info.plistpara fazer esse trabalho (que é o padrão).

Damnum
fonte
@Como isso acontece no swift3?
aeronaves
1

Solução Swift 3 iOS 10:

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

A maioria das respostas não inclui uma boa implementação de childViewControllerForStatusBarStylemé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
}
Timur Bernikovich
fonte
1

No meu caso, apresentei acidentalmente o View / Navigation Controller como UIModalPresentationStyle.overFullScreen, o que faz com que preferredStatusBarStylenão seja chamado. Depois de voltar para UIModalPresentationStyle.fullScreen, tudo funciona.

Casey
fonte
1

Quanto ao iOS 13.4, o preferredStatusBarStylemétodo na UINavigationControllercategoria 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];
Kevin Flachsmann
fonte
0

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.

arturgrigor
fonte
0

Se alguém encontrar esse problema com o UISearchController. Apenas crie uma nova subclasse de UISearchController e adicione o código abaixo nessa classe:

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

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.

Richard Garfield
fonte
Definir UIViewControllerBasedStatusBarAppearance como YES no plist do projeto fez toda a diferença para mim. Eu tinha esquecido disso.
filo
0

Desde o Xcode 11.4, a substituição da preferredStatusBarStylepropriedade em uma extensão UINavigationController não funciona mais, pois não será chamada.

Definir a barStylede navigationBarque .blackobras 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 barStylepara preto, a userInterfaceStylevisualização incorporada na barra de navegação sempre terá, userInterfaceStyle.darkindependentemente do userInterfaceStyleaplicativo.

A solução adequada que eu encontro é adicionar uma subclasse UINavigationControllere substituir preferredStatusBarStylelá. Se você usar esse UINavigationController personalizado para todas as suas visualizações, estará no lado de salvar.

PatrickDotStar
fonte
-1

O NavigationController ou o TabBarController são os que precisam fornecer o estilo. Aqui está como eu resolvi: https://stackoverflow.com/a/39072526/242769

aryaxt
fonte
Se você acha que isso é uma duplicata de outra pergunta, feche-a como duplicada