favoriteStatusBarStyle non viene chiamato


257

Ho seguito questo thread per sovrascrivere -preferredStatusBarStyle, ma non viene chiamato. Ci sono opzioni che posso modificare per abilitarlo? (Sto usando XIB nel mio progetto.)


Non viene chiamato in quale contesto: simulatore? su un dispositivo?
bneely

@beneely entrambi.
trgoofi,

Stai utilizzando il simulatore iOS 7, un dispositivo iOS 7 e l'SDK di base è 7.0?
bneely

@bneely iOS SDK 7.0 è mostrato sotto il nome del mio progetto, significa che il mio SDK di base è 7.0?
trgoofi,

Nelle impostazioni di compilazione, "Base SDK" è dove viene impostato il valore. Sembra che il tuo progetto sia impostato su 7.0.
bneely

Risposte:


117

Possibile causa alla radice

Ho avuto lo stesso problema e ho capito che stava accadendo perché non stavo impostando il controller della vista radice nella finestra della mia applicazione.

Quello UIViewControllerin cui avevo implementato preferredStatusBarStyleera usato in a UITabBarController, che controllava l'aspetto delle viste sullo schermo.

Quando ho impostato il controller della vista principale per puntare a questo UITabBarController, le modifiche alla barra di stato hanno iniziato a funzionare correttamente, come previsto (e il preferredStatusBarStylemetodo veniva chiamato).

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

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

Metodo alternativo (obsoleto in iOS 9)

In alternativa, puoi chiamare uno dei seguenti metodi, a seconda dei casi, in ciascuno dei tuoi controller di visualizzazione, a seconda del colore di sfondo, invece di dover usare setNeedsStatusBarAppearanceUpdate:

[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

o

[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];

Nota che dovrai anche impostare UIViewControllerBasedStatusBarAppearancesu NOnel file plist se usi questo metodo.


2
Ho il tuo stesso problema, non sto impostando il controller della vista radice. Come diavolo l'hai trovato?
trgoofi,

1
Sospettavo che qualcosa nel framework non ricevesse la notifica setNeedsStatusBarAppearanceUpdate: i miei sospetti sono stati confermati quando ho apportato questa modifica.
AbdullahC

2
Un problema correlato che ho riscontrato in un'app era un controller di visualizzazione con un controller di visualizzazione figlio a schermo intero che non sostituiva childViewControllerForStatusBarStyle e childViewControllerForStatusBarHidden per restituire quel controller di visualizzazione figlio. Se si dispone della propria gerarchia del controller di visualizzazione, è necessario fornire questi metodi per informare il sistema di quale controller di visualizzazione deve essere utilizzato per determinare lo stile della barra di stato.
Jon Steinmetz,

l'impostazione del rootviewcontroller non cambia nulla. Dovresti lavorare con il commento di Jon. E fai attenzione quando chiami setneedsstatusbarappearanceUpdate. Dovresti chiamarlo dal genitore per lavorare.
dooz,

1
@Hippo sei un genio !! Come hai scoperto che non è stato impostato il rootviewcontroller?
ViruMax,

1019

Per chiunque utilizzi un UINavigationController:

Il UINavigationControllernon inoltra le preferredStatusBarStylechiamate ai controller della vista figlio. Invece gestisce il proprio stato - come dovrebbe, sta disegnando nella parte superiore dello schermo in cui vive la barra di stato e quindi dovrebbe esserne responsabile. Pertanto attuazionepreferredStatusBarStyle nei VC all'interno di un controller di navigazione non farà nulla, non verranno mai chiamati.

Il trucco è ciò che gli UINavigationControllerusi per decidere per cosa restituire UIStatusBarStyleDefaulto UIStatusBarStyleLightContent. Basa questo sul suo UINavigationBar.barStyle. Il predefinito (UIBarStyleDefault ) indica la UIStatusBarStyleDefaultbarra di stato in primo piano scuro . E UIBarStyleBlackdarà aUIStatusBarStyleLightContent barra di stato.

TL; DR:

Se vuoi UIStatusBarStyleLightContentun UINavigationControlleruso:

self.navigationController.navigationBar.barStyle = UIBarStyleBlack;

59
Bello! Si noti che preferredStatusBarStyleverrà effettivamente chiamato sul controller di visualizzazione figlio se si nasconde la barra di navigazione (impostata navigationBarHiddensu YES), esattamente come appropriato.
Patrick Pijnappel,

25
Grazie per questa risposta Se vuoi impostare il barStyle per tutte le tue barre di navigazione, chiama[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack]
Thomas Desert il

15
Risposta perfetta Nessuna delle altre risposte su SO ha preso in considerazione UINavigationController. 2 ore di sbattere la testa contro la tastiera.
Ryan Alford,

10
Complimenti a @Patrick per aver indicato che navigationBarHiddenset to YESavrà effettivamente preferredStatusBarStylechiamato, e un avvertimento per quelli che potrebbero inciampare su questo: funziona con navigationBarHidden, ma non con navigationBar.hidden!
Jcaron,

4
dovrebbe essere ovvio, ma è necessario anche "Visualizza l'aspetto della barra di stato basata sul controller" impostato su SÌ in Info.plist affinché funzioni.
Code Baller,

99

Quindi ho effettivamente aggiunto una categoria a UINavigationController ma ho usato i metodi:

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

e questi hanno restituito l'attuale UIViewController visibile. Ciò consente all'attuale controller di visualizzazione visibile di impostare il proprio stile / visibilità preferito.

Ecco uno snippet di codice completo per questo:

In Swift:

extension UINavigationController {

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

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

In Objective-C:

@interface UINavigationController (StatusBarStyle)

@end

@implementation UINavigationController (StatusBarStyle)

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

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

@end

E per buona misura, ecco come viene implementato quindi in un UIViewController:

In Swift

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

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

In Objective-C

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

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

Infine, assicurati che il tuo plist dell'app NON abbia "Visualizza aspetto barra di stato basata sul controller" impostato su NO. O eliminare quella linea o impostarla su SÌ (che credo sia l'impostazione predefinita ora per iOS 7?)


Sembra che return self.topViewController;return self.visibleViewController;
funzioni

visibleViewController può restituire il controller modale attualmente presentato quando lo si elimina. Che è triste. Usa topViewController.
Ben Sinclair,

1
@ d.lebedev ok, ma non credo che nessuno di questi problemi si applichi qui. Non è necessario chiamare superquesto metodo e in realtà si desidera modificare il comportamento di tutti i controller di questo tipo
ed '

1
questo non funziona per me su iOS 9.3. Immagino che questo sia il problema: questo problema ha un significato particolare perché molte delle classi Cocoa sono implementate usando categorie. Un metodo definito dal framework che si tenta di ignorare potrebbe essere stato implementato in una categoria e pertanto l'implementazione che ha la precedenza non è definita.
vikingosegundo,

2
Questo è sbagliato e si rompe in iOS 13.4. Perché l'estensione delle classi C oggettive in Swift è implementata attraverso le categorie Obiettivo C. Non è consigliabile ignorare i metodi attraverso le categorie dell'obiettivo C. Vedi stackoverflow.com/a/38274660/2438634
Marc Etcheverry il

79

Per chiunque abbia ancora problemi con questo, questa semplice estensione in rapido dovrebbe risolvere il problema per te.

extension UINavigationController {
    override open var childForStatusBarStyle: UIViewController? {
        return self.topViewController
    }
}

10
Meriti una medaglia.
nikans

2
Grazie mille amico. Stavo restituendo visibleViewController invece senza successo.
Fábio Salata,

1
Questo è oro Ho un controller di navigazione incorporato in una barra delle schede e l'ho appena lanciato in un file e ora posso cambiare l'aspetto della barra di stato dove voglio.
Vahid Amiri,

2
Questo è sbagliato e si rompe in iOS 13.4. Perché l'estensione delle classi C oggettive in Swift è implementata attraverso le categorie Obiettivo C. Non è consigliabile ignorare i metodi attraverso le categorie dell'obiettivo C. Vedi stackoverflow.com/a/38274660/2438634
Marc Etcheverry il

1
@MarcEtcheverry questa particolare istanza non era sbagliata. Il fatto è che le sottoclassi di altri oggetti / protocolli come UINavigationController non avevano implementato in precedenza tali conflitti in caso di invio dinamico. Non vi erano valori predefiniti o implementazioni all'interno delle sottoclassi effettive, motivo per cui questo è stato il modo più pulito di implementarlo in un'app senza creare una dipendenza (punto) non necessaria. Sfortunatamente, 13.4 sembra aver cambiato questo comportamento. Sto indovinando dietro le quinte che hanno un controllo o un'implementazione che è inesistente da anni .........
TheCodingArt

20

La mia app ha usato tutti e tre: UINavigationController , UISplitViewController, UITabBarController, quindi questi tutti sembrano prendere il controllo sopra la barra di stato e causerà preferedStatusBarStyledi non essere chiamati per i loro figli. Per ignorare questo comportamento, puoi creare un'estensione come il resto delle risposte ha menzionato. Ecco un'estensione per tutti e tre, in Swift 4. Vorrei che Apple fosse più chiara su questo genere di cose.

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

Modifica: aggiornamento per le modifiche dell'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
    }
}

1
Questa è l'unica soluzione che funziona. Tutte le risposte su SO puntano alla soluzione standard che non funzionerà per nessuna app con NavigationController. Grazie!!!
Houman,

L'uso delle estensioni per l'override è semplicemente sbagliato. Non è sicuro Esistono più soluzioni più semplici. Utilizzare invece una sottoclasse.
Sulthan,

2
Questo è sbagliato e si rompe in iOS 13.4. Perché l'estensione delle classi C oggettive in Swift è implementata attraverso le categorie Obiettivo C. Non è consigliabile ignorare i metodi attraverso le categorie dell'obiettivo C. Vedi stackoverflow.com/a/38274660/2438634
Marc Etcheverry il

1
@MarcEtcheverry questa particolare istanza non era sbagliata. Il fatto è che le sottoclassi di altri oggetti / protocolli come UINavigationController non avevano implementato in precedenza tali conflitti in caso di invio dinamico. Non vi erano valori predefiniti o implementazioni all'interno delle sottoclassi effettive, motivo per cui questo è stato il modo più pulito di implementarlo in un'app senza creare una dipendenza (punto) non necessaria. Sfortunatamente, 13.4 sembra aver cambiato questo comportamento. Sto indovinando dietro le quinte che hanno un controllo o un'implementazione che è inesistente da anni .........
TheCodingArt

15

La risposta di Tyson è corretta per cambiare il colore della barra di stato in biancoUINavigationController .

Se qualcuno vuole ottenere lo stesso risultato scrivendo il codice, AppDelegateallora usa il codice qui sotto e scrivilo dentroAppDelegate's didFinishLaunchingWithOptions metodo.

E non dimenticate di impostare l' UIViewControllerBasedStatusBarAppearanceaYES nel file .plist, altrimenti la modifica non rifletterà.

Codice

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

     return YES;
}

14

Su un UINavigationController, preferredStatusBarStylenon viene chiamato perché topViewControllerè preferito self. Quindi, per essere preferredStatusBarStylechiamato su un UINavigationController, devi cambiarlochildViewControllerForStatusBarStyle .

Raccomandazione

Sostituisci il tuo UINavigationController nella tua classe:

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

Alternativa non raccomandata

Per farlo per tutti gli UINavigationController, è possibile ignorare un'estensione (avviso: riguarda UIDocumentPickerViewController, UIImagePickerController, ecc.), Ma probabilmente non dovresti farlo secondo la documentazione di Swift :

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

11

Oltre alla risposta di Serenn, se stai presentando un controller di visualizzazione con modalPresentationStyle(ad esempio .overCurrentContext), dovresti anche chiamarlo sul controller di visualizzazione appena presentato:

presentedViewController.modalPresentationCapturesStatusBarAppearance = true

Non dimenticare di sostituire anche il preferredStatusBarStylecontroller di visualizzazione presentato.


9

Un'aggiunta alla risposta di Ippona: se stai usando un UINavigationController, probabilmente è meglio aggiungere una 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

Quella soluzione è probabilmente meglio che passare a comportamenti presto deprecati.


Non farlo, funziona per ora ma potrebbe interrompere il comportamento futuro. Basta cambiare lo stile navBar - vedi la mia risposta stackoverflow.com/a/19513714/505457
Tyson

2
È necessario utilizzare la sottoclasse, non la categoria.
shuiyouren,

2Tyson: Perché interromperà il comportamento futuro? favoriteStatusBarStyle: è il metodo preferito di Apple per impostare lo stile della barra di stato.
Artem Abramov,

2shuiyouren: Perché dovrei aumentare la complessità eseguendo la sottoclasse se posso semplicemente usare una categoria e includerla in ogni luogo dove voglio? Comunque, questa è una questione di architettura, non di implementazione.
Artem Abramov,

2
@ArtemAbramov Perché UINavigationController implementa preferredStatusBarStylee fa la logica specifica di UINavigationController. In questo momento questa logica si basa navigationBar.barStylema posso vedere ulteriori controlli aggiunti (ad es. UISearchDisplayControllerPassare alla modalità di navigazione della barra di navigazione). Sostituendo la logica predefinita perdi tutte queste funzionalità e ti lasci aperto per fastidiosi momenti "wtf" in futuro. Vedi la mia risposta sopra per il modo corretto di farlo pur supportando il comportamento del controller di navigazione integrato.
Tyson,

9

Swift 4.2 e versioni successive

Come menzionato nella risposta selezionata , la causa principale è controllare l'oggetto controller della vista radice della finestra.

Possibili casi della struttura del flusso

  • L'oggetto UIViewController personalizzato è il controller della vista radice della finestra

    Il controller della vista radice della finestra è un oggetto UIViewController e aggiunge o rimuove ulteriormente il controller di navigazione o tabController in base al flusso dell'applicazione.

    Questo tipo di flusso viene in genere utilizzato se l'app ha un flusso di pre-accesso nello stack di navigazione senza schede e il flusso di accesso post con schede e possibilmente ogni scheda contiene ulteriormente il controller di navigazione.

  • L'oggetto TabBarController è il controller della vista radice della finestra

    Questo è il flusso in cui il controller della vista radice della finestra è tabBarController probabilmente ogni scheda contiene ulteriormente il controller di navigazione.

  • L'oggetto NavigationController è il controller della vista radice della finestra

    Questo è il flusso in cui il controller della vista radice della finestra è navigationController.

    Non sono sicuro se esiste la possibilità di aggiungere un controller della barra delle schede o un nuovo controller di navigazione in un controller di navigazione esistente. Ma in tal caso, dobbiamo passare il controllo dello stile della barra di stato al contenitore successivo. Quindi, ho aggiunto lo stesso controllo nell'estensione UINavigationController per trovarechildForStatusBarStyle

Utilizza le seguenti estensioni, gestisce tutti gli scenari sopra indicati -

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
    }
}
  • Non è necessario UIViewControllerBasedStatusBarAppearancedigitare info.plistcome vero per impostazione predefinita

Punti da considerare per flussi più complessi

  • Se si presenta il nuovo flusso in modo modale, si stacca dal flusso di stile della barra di stato esistente. Supponiamo quindi che tu stia presentando un NewFlowUIViewControllere quindi aggiungi un nuovo controller di navigazione o tabBar a NewFlowUIViewController, quindi aggiungi l'estensione di NewFlowUIViewControlleranche per gestire ulteriormente lo stile della barra di stato del controller.

  • Se si imposta modalPresentationStyle diverso da fullScreenquando si presenta in modalità modale, è necessario impostare modalPresentationCapturesStatusBarAppearancesu true in modo che il controller di visualizzazione presentato debba ricevere il controllo dell'aspetto della barra di stato.


Risposta eccellente!
Amin Benarieb,

3
Questo è sbagliato e si rompe in iOS 13.4. Perché l'estensione delle classi C oggettive in Swift è implementata attraverso le categorie Obiettivo C. Non è consigliabile ignorare i metodi attraverso le categorie dell'obiettivo C. Vedi stackoverflow.com/a/38274660/2438634
Marc Etcheverry il

@MarcEtcheverry questa particolare istanza non era sbagliata. Il fatto è che le sottoclassi di altri oggetti / protocolli come UINavigationController non avevano implementato in precedenza tali conflitti in caso di invio dinamico. Non vi erano valori predefiniti o implementazioni all'interno delle sottoclassi effettive, motivo per cui questo è stato il modo più pulito di implementarlo in un'app senza creare una dipendenza (punto) non necessaria. Sfortunatamente, 13.4 sembra aver cambiato questo comportamento. Sto indovinando dietro le quinte che ora hanno un controllo o un'implementazione che è inesistente da anni .........
TheCodingArt

8

Soluzioni iOS 13

UINavigationController è una sottoclasse di UIViewController (chi lo sapeva 🙃)!

Pertanto, quando si presentano controller di visualizzazione incorporati nei controller di navigazione, non si presentano realmente i controller di visualizzazione incorporati; stai presentando i controller di navigazione! UINavigationController, come sottoclasse di UIViewController, eredita preferredStatusBarStyleechildForStatusBarStyle , che è possibile impostare come desiderato.

Uno dei seguenti metodi dovrebbe funzionare:

  1. Annulla completamente la modalità oscura
    • Nel tuo info.plist , aggiungi la seguente proprietà:
      • Chiave - UIUserInterfaceStyle (alias "Stile interfaccia utente")
      • Valore - Luce
  2. Ignora preferredStatusBarStyledentroUINavigationController

    • preferredStatusBarStyle( doc ) - Lo stile preferito della barra di stato per il controller di visualizzazione
    • Sottoclasse o estensione UINavigationController

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

      O

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

    • childForStatusBarStyle( doc ): chiamato quando il sistema deve utilizzare il controller di visualizzazione per determinare lo stile della barra di stato
    • Secondo la documentazione di Apple,

      "Se il controller della vista contenitore deriva il suo stile dalla barra di stato da uno dei controller della vista figlio, [sovrascrivi questa proprietà] e restituisce quel controller vista figlio. Se restituisci zero o non ignori questo metodo, viene utilizzato lo stile della barra di stato per sé Se il valore restituito da questo metodo cambia, chiama il metodo setNeedsStatusBarAppearanceUpdate (). "

    • In altre parole, se non si implementa la soluzione 3 qui, il sistema tornerà alla soluzione 2 sopra.
    • Sottoclasse o estensione UINavigationController

      class MyNavigationController: UINavigationController {
          override var childForStatusBarStyle: UIViewController? {
              topViewController
          }
      }

      O

      extension UINavigationController {    
          open override var childForStatusBarStyle: UIViewController? {
              topViewController
          }
      }
    • Puoi restituire qualsiasi controller di visualizzazione che desideri sopra. Raccomando una delle seguenti:

      • topViewController(of UINavigationController) ( doc ) - Il controller di visualizzazione nella parte superiore dello stack di navigazione
      • visibleViewController(of UINavigationController) ( doc ) - Il controller di vista associato alla vista attualmente visibile nell'interfaccia di navigazione (suggerimento: questo può includere "un controller di vista che è stato presentato modalmente sopra il controller di navigazione stesso")

Nota: se decidi di effettuare una sottoclasse UINavigationController, ricorda di applicare quella classe ai tuoi controllori di navigazione tramite l'ispettore identità in IB.

PS Il mio codice utilizza la sintassi di Swift 5.1 😎


La barra di stato diventa nera dopo la rotazione dello schermo. Qualche idea sul perché? Questo succede solo sul simulatore di iPad Pro.
Pedro Paulo Amorim,

@PedroPauloAmorim, puoi fornire maggiori informazioni? Come viene presentato il controller della vista dall'alto (modale, schermo intero, spettacolo)? È nidificato all'interno di un controller di navigazione? Il testo sta diventando nero o anche lo sfondo? Cosa stai cercando di realizzare?
Andrew Kirna,

Ho impostato la barra di stato della luce in tutta la mia app. Si illumina in due rotazioni, nella terza diventa scuro e non ritorna mai alla luce, anche forzando a ridisegnarlo. Sta succedendo sul simulatore di iPad Pro. Le visualizzazioni vengono presentate a schermo intero e non sono nidificate all'interno di un controller di navigazione. Solo il testo diventa scuro.
Pedro Paulo Amorim,

Come stai impostando la barra di stato della luce in primo luogo?
Andrew Kirna

3
La tua sostituzione tramite estensione non è una vera sostituzione. È un uso improprio non sicuro della lingua. Questo può rompersi molto facilmente.
Sulthan,

7

@ di serenn risposta di sopra è ancora ottima per il caso di UINavigationController. Tuttavia, per swift 3 le funzioni childViewController sono state modificate in vars. Quindi il UINavigationControllercodice di estensione dovrebbe essere:

override open var childViewControllerForStatusBarStyle: UIViewController? {
  return topViewController
}

override open var childViewControllerForStatusBarHidden: UIViewController? {
  return topViewController
}

E poi nel controller della vista che dovrebbe dettare lo stile della barra di stato:

override var preferredStatusBarStyle: UIStatusBarStyle {
   return .lightContent
}

2
Questo è sbagliato e si rompe in iOS 13.4. Perché l'estensione delle classi C oggettive in Swift è implementata attraverso le categorie Obiettivo C. Non è consigliabile ignorare i metodi attraverso le categorie dell'obiettivo C. Vedi stackoverflow.com/a/38274660/2438634
Marc Etcheverry il

@MarcEtcheverry questa particolare istanza non era sbagliata. Il fatto è che le sottoclassi di altri oggetti / protocolli come UINavigationController non avevano implementato in precedenza tali conflitti in caso di invio dinamico. Non vi erano valori predefiniti o implementazioni all'interno delle sottoclassi effettive, motivo per cui questo è stato il modo più pulito di implementarlo in un'app senza creare una dipendenza (punto) non necessaria. Sfortunatamente, 13.4 sembra aver cambiato questo comportamento. Sto indovinando dietro le quinte che ora hanno un controllo o un'implementazione che è inesistente da anni .........
TheCodingArt


4

UIStatusBarStyle in iOS 7

La barra di stato in iOS 7 è trasparente, la vista dietro mostra attraverso.

Lo stile della barra di stato si riferisce alle apparenze del suo contenuto. In iOS 7, il contenuto della barra di stato è scuro ( UIStatusBarStyleDefault) o chiaro ( UIStatusBarStyleLightContent). Entrambi UIStatusBarStyleBlackTranslucente UIStatusBarStyleBlackOpaquesono deprecati in iOS 7.0. Usa UIStatusBarStyleLightContentinvece.

Come cambiare UIStatusBarStyle

Se sotto la barra di stato è presente una barra di navigazione, lo stile della barra di stato verrà adattato allo stile della barra di navigazione ( UINavigationBar.barStyle):

In particolare, se lo stile della barra di navigazione è UIBarStyleDefault, lo stile della barra di stato sarà UIStatusBarStyleDefault; se lo stile della barra di navigazione è UIBarStyleBlack, lo sarà lo stile della barra di statoUIStatusBarStyleLightContent .

Se non esiste una barra di navigazione sotto la barra di stato, lo stile della barra di stato può essere controllato e modificato da un controller di visualizzazione individuale mentre l'app è in esecuzione.

- [UIViewController preferredStatusBarStyle]è un nuovo metodo aggiunto in iOS 7. Può essere sostituito per restituire lo stile preferito della barra di stato:

- (UIStatusBarStyle)preferredStatusBarStyle
  {
      return UIStatusBarStyleLightContent;
  }

Se lo stile della barra di stato deve essere controllato da un controller di visualizzazione figlio anziché da sé, eseguire l'override -[UIViewController childViewControllerForStatusBarStyle] l' per restituire quel controller di visualizzazione figlio.

Se si preferisce rinunciare a questo comportamento e impostare lo stile della barra di stato utilizzando il -[UIApplication statusBarStyle]metodo, aggiungere la UIViewControllerBasedStatusBarAppearancechiave al Info.plistfile di un'app e assegnargli il valore NO.


3

Se qualcuno utilizza un controller di navigazione e desidera che tutti i controller di navigazione abbiano lo stile nero, puoi scrivere un'estensione su UINavigationController come questa in Swift 3 e si applicherà a tutti i controller di navigazione (invece di assegnarlo a un controller in un tempo).

extension UINavigationController {

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

        self.navigationBar.barStyle = UIBarStyle.black
    }

}

1
E se la barra di navigazione fosse nascosta?
Slavcho,

1
Perché ho bisogno che la navigazione sia nascosta e che la barra di stato sia visibile.
Slavcho,

1

In Swift per qualsiasi tipo di UIViewController:

Nel tuo AppDelegateset:

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

myRootControllerpuò essere di qualsiasi tipo UIViewController, ad esempio UITabBarControllero UINavigationController.

Quindi, sovrascrivi questo controller di root in questo modo:

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

Ciò modificherà l'aspetto della barra di stato nell'intera app, poiché il controller di root è l'unico responsabile dell'aspetto della barra di stato.

Ricorda di impostare la proprietà View controller-based status bar appearancesu YES nel tuo Info.plistper farlo funzionare (che è l'impostazione predefinita).


@Come funziona in swift3?
aereo il

1

Swift 3 iOS 10 Solution:

override var preferredStatusBarStyle: UIStatusBarStyle {
    return .lightContent
 }

1

La maggior parte delle risposte non include una buona implementazione del childViewControllerForStatusBarStylemetodo per UINavigationController. Secondo la mia esperienza, dovresti gestire casi come quando il controller di visualizzazione trasparente viene presentato sul controller di navigazione. In questi casi dovresti passare il controllo al tuo controller modale ( visibleViewController), ma non quando sta scomparendo.

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

1

Nel mio caso, ho presentato accidentalmente View / Navigation Controller come UIModalPresentationStyle.overFullScreen, il che provoca la preferredStatusBarStylemancata chiamata. Dopo averlo riportato su UIModalPresentationStyle.fullScreen, tutto funziona.


1

Come per iOS 13.4 il preferredStatusBarStylemetodo inUINavigationController categoria non verrà chiamato, lo swizzling sembra essere l'unica opzione senza la necessità di utilizzare una sottoclasse.

Esempio:

Intestazione di categoria:

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

Implementazione:

#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

Utilizzo in AppDelegate.h:

#import "UINavigationController+StatusBarStyle.h"

[UINavigationController setUseLightStatusBarStyle];

0

Ecco il mio metodo per risolvere questo.

Definire un protocollo chiamato AGViewControllerAppearance .

AGViewControllerAppearance.h

#import <Foundation/Foundation.h>

@protocol AGViewControllerAppearance <NSObject>

@optional

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

@end

Definire una categoria su UIViewController chiamata 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

Ora è il momento di dire che il controller di visualizzazione sta implementando AGViewControllerAppearance protocollo .

Esempio:

@interface XYSampleViewController () <AGViewControllerAppearance>

... the rest of the interface

@end

Naturalmente, è possibile implementare il resto dei metodi ( showsStatusBar , animatesStatusBarVisibility , prefferedStatusBarAnimation ) dal protocollo e UIViewController + upgrade farà la personalizzazione adeguata in base ai valori forniti da loro.


0

Se qualcuno incontra questo problema con UISearchController. Basta creare una nuova sottoclasse di UISearchController, quindi aggiungere il codice seguente in quella classe:

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

0

Si noti che quando si utilizza il self.navigationController.navigationBar.barStyle = UIBarStyleBlack; soluzione

assicurati di andare al tuo plist e impostare "Visualizza aspetto della barra di stato basata sul controller" su SÌ. Se è NO non funzionerà.


L'impostazione di UIViewControllerBasedStatusBarAppearance su YES nel plist del progetto ha fatto la differenza per me. L 'avevo dimenticato.
filo

0

Da Xcode 11.4, sovrascrivendo il preferredStatusBarStyle proprietà in un'estensione UINavigationController non funziona più poiché non verrà chiamata.

L'impostazione di barStyledi navigationBarsu .blackfunziona davvero, ma ciò aggiungerà effetti collaterali indesiderati se aggiungi viste secondarie alla barra di navigazione che possono avere aspetti diversi per la modalità chiara e scura. Perché impostando il barStylenero su, la userInterfaceStylevista di quella che è incorporata nella barra di navigazione avrà sempre userInterfaceStyle.darkindipendentemente userInterfaceStyledall'app.

La soluzione corretta che mi viene in mente è aggiungendo una sottoclasse di UINavigationController e l'override preferredStatusBarStylelì. Se poi usi questo UINavigationController personalizzato per tutte le tue visualizzazioni, sarai sul lato di salvataggio.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.