UIStatusBarStyle PreferredStatusBarStyle nie działa w systemie iOS 7

110

W mojej aplikacji iPhone zbudowany z Xcode 5 dla iOS 7 ustawić UIViewControllerBasedStatusBarAppearance=YESw info.plist, aw moim ViewControllermam ten kod:

-(UIStatusBarStyle) preferredStatusBarStyle
{
    return UIStatusBarStyleLightContent;
}

Ale pasek stanu jest nadal czarny na czarnym tle.

Wiem, że można to zmienić app-wide poprzez ustawienie UIViewControllerBasedStatusBarAppearance=NOw info.plist, ale faktycznie trzeba zmienić to na viewControllerza viewControllerpodstawę przy starcie.

Andrew Smith
źródło
Cześć, mam ten sam problem, o którym wspomniałeś w pytaniu. Masz rozwiązanie? Proszę, daj mi to.
Noundla Sandeep
Możesz
rzucić

Odpowiedzi:

281

Odkryłem, że jeśli Twój ViewController znajduje się wewnątrz navigationController, to navigationController navigationBar.barStyleokreśla statusBarStyle.

Ustawianie navigationBar na barStylecelu UIBarStyleBlackTranslucentdadzą biały pasek statusu tekstu (np. UIStatusBarStyleLightContent), A UIBarStyleDefaultda czarny tekst paska stanu (tzn. UIStatusBarStyleDefault).

Zauważ, że dotyczy to nawet jeśli całkowicie zmienisz kolor NavigationBar za pomocą jego barTintColor.

mxcl
źródło
to ma dla mnie sens ... świetnie
Nick,
14
Wierzę, że to dlatego, że UINavigationController„s preferredStatusBarStylenie zadzwonić do ViewController przez to gospodarze, a zamiast tego po prostu zwraca na podstawie jego navigationBarStyle.
mxcl
W tym przypadku widok nie znajduje się w kontrolerze nawigacyjnym.
Andrew Smith
Bardzo sprzeczne z intuicją jest myślenie, że styl paska ma pierwszeństwo przed metodą zaimplementowaną w kontrolerze widoku i tylko podczas prezentowania widoków modalnych!
Matej
3
UIBarStyleBlackTranslucent jest przestarzałe, użyj UIBarStyleBlackzamiast tego
Nhon Nguyen
87

OK, oto sztuczka. Musisz dodać klawisz „Wyświetl pasek stanu oparty na kontrolerze” i ustawić wartość na Nie.

Jest to sprzeczne z tym, co wydaje się mieć znaczenie tego klucza, ale nawet jeśli ustawisz wartość na No, nadal możesz zmienić wygląd paska stanu i to, czy jest on wyświetlany, czy nie w dowolnym kontrolerze widoku. Więc działa jak „Tak”, ale ustaw go na „Nie”!

Teraz mogę uzyskać biały lub ciemny pasek stanu.

Andrew Smith
źródło
6
Dla mnie to było złe. Klucz musiał być ustawiony na „Tak”, jak można się było spodziewać. Jestem na Xcode 5.1 iOS 7.1, więc może to się zmieniło.
joel.d
Używam również Xcode 5.1 i iOS 7.1 i NIE działało dla mnie ... DZIWNE.
Arjun Mehta
Gdzie mam dodać ten klucz?
Hadu
W Twoim pliku [AppName] -Info.plist
Saren Inden
1
Działa dobrze, gdy przycisk „Wyświetl pasek stanu oparty na kontrolerze” jest ustawiony na „TAK” w Xcode6.0, iOS 8.0
bpolat
77

Do preferredStatusBarStyle()pracy wewnątrz UINavigationControlleri UITabBarControllerdodaję następujący kod, który pobierze preferowany styl paska stanu z aktualnie widocznego kontrolera widoku.

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

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

Dla Swift 3 nie są to metody, ale właściwości:

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

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

Nazwy właściwości Swift 4.2 zostały zmienione:

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

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

Stosowanie

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
źródło
6
To jest zdecydowanie najlepsza odpowiedź (dla aplikacji, które używają UINavigationController lub UITabBarController
Kesava
1
jaki jest pożytek z tego?
AnBisw
@Annjawn te metody są używane przez UIKit. Nie musisz nic robić poza dodaniem go do swojego projektu.
Daniel Wood
@DanielWood tak, domyśliłem się tego i zupełnie zapomniałem, że użyłem dokładnie tego samego w jednym z moich poprzednich projektów, chociaż trochę inaczej.
AnBisw
To rzeczywiście najlepsza odpowiedź
Musa almatri
33

Być może przyjdę do tego trochę późno, ale na wypadek, gdyby ktoś inny szukał działającego i zweryfikowanego rozwiązania dla całej aplikacji.

@mxcl ma rację, opisując, dlaczego tak się dzieje. Aby to poprawić, po prostu tworzymy rozszerzenie (lub kategorię w obj-c), które przesłania metodę preferowanąSatusBarStyle () metody UINavigationController. Oto przykład w Swift:

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

Ten kod po prostu wyodrębnia pierwszy kontroler widoku (główny kontroler widoku) i rozpakowuje go (w obj-c po prostu sprawdź, czy nie jest nil). Jeśli rozpakowanie powiedzie się (nie zerowe), pobieramy rootViewControllers preferowanyStatusBarStyle. W przeciwnym razie po prostu zwracamy wartość domyślną.

Mam nadzieję, że pomoże to każdemu, kto może tego potrzebować.

Kyle Begeman
źródło
2
W Swift 2.0 należy usunąć „as? UIViewController” z instrukcji warunku.
Thomás Calmon
Świetnie, dokonałem jednej modyfikacji oprócz usunięcia instrukcji "as", zmieniłem ją z "pierwsza" na "ostatnia" w ten sposób każdy kontroler widoku widziany przez użytkownika na szczycie stosu będzie miał możliwość kontrolowania kolor paska stanu. Świetna robota, dzięki za udostępnienie!
Unome
1
Jeśli twój kontroler nawigacyjny nie ma żadnych kontrolerów widoku, spowodowałoby to nieskończoną pętlę. return self.preferredStatusBarStyle()odwołałby się do tej samej metody.
BearMountain
1
W moim przypadku zamiast używać rootViewController, użyłem topViewController, ponieważ podczas stosu styl może się zmienić.
Ric Santos
2
@Unome visibleViewControllerbyłby jeszcze lepszy
Cœur
21

Aby podać więcej szczegółów w zaakceptowanej odpowiedzi, umieść następujący wiersz w didFinishLaunchingWithOptions:metodzie delegata aplikacji :

[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;

Następnie w swoim Info.plist dodaj View controller-based status bar appearancei ustaw na NO.

Uważam, że tak należy to zrobić, NIE z poziomu kontrolera nawigacji, jeśli chcesz mieć ten sam kolor paska stanu dla całej aplikacji. Możesz mieć ekrany, które niekoniecznie są osadzone w UINavigationControllerinnej UINavigationControllerpodklasie lub gdzie indziej, i inne rzeczy.

EDYCJA : Możesz to również zrobić bez wpisywania kodu: https://stackoverflow.com/a/18732865/855680

Matthew Quiros
źródło
1
Zauważ, że ten sposób jest przestarzały z IOS 9.0
Mohamed Salah
10

W viewDidLoad po prostu napisz to

[self setNeedsStatusBarAppearanceUpdate];

po prostu zrób to i zadziała

czy możesz tego spróbować

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

Jeszcze jedna rzecz, którą zauważyłem w twoim pytaniu, że napisałeś taką metodę

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

ale tak powinno być

-(UIStatusBarStyle)preferredStatusBarStyle{ 
    return UIStatusBarStyleLightContent; 
} 
Użytkownik 1531343
źródło
Powoduje to wywołanie metody preferowanejStatusBarStyle, ale nadal pasek stanu jest czarny.
Andrew Smith
zobacz moją zaktualizowaną odpowiedź ... daj mi szybko znać, czy to działa, czy nie
Użytkownik 1531343
Moje pierwotne pytanie wyraźnie mówi, że muszę przeglądać za pomocą kontroli widoku paska stanu.
Andrew Smith
czy możesz sprawdzić swój kod w odniesieniu do mojego zaktualizowanego pytania?
Użytkownik 1531343
1
[self setNeedsStatusBarAppearanceUpdate];taka świetna metoda, dziękuję!
Supertecnoboff
6

Oto jak to rozwiązałem. Zazwyczaj navigationController lub tabBarController są tymi, które decydują o wyglądzie paska stanu (ukryty, kolor itp.).

Skończyło się na tym, że podklasowałem kontroler nawigacji i zastąpiłem preferowanyStatusBarStyle. jeśli bieżący widoczny ViewContorller implementuje StatusBarStyleHandler, proszę o wartość, która ma być używana jako styl, jeśli nie, po prostu zwracam wartość domyślną.

Sposób wyzwalania aktualizacji wyglądu paska stanu polega na wywołaniu, setNeedsStatusBarAppearanceUpdatektóre wyzwala preferredStatusBarStyleponownie i aktualizuje interfejs użytkownika zgodnie z tym, co zwraca metoda

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

Następnie użycie

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
źródło
Ten post byłby znacznie ulepszony, gdybyś mógł wyjaśnić, dlaczego i jak to rozwiązuje problem.
Zamiast tworzyć podklasy UINavigationController, możesz po prostu utworzyć rozszerzenie dla UINavigationController i osiągnąć ten sam wynik bez konieczności tworzenia podklasy.
wilforeal
4

1) Jedno ustawienie dla całego projektu:

Jeśli to możliwe, usuń UIViewControllerBasedStatusBarAppearanceparę klucz-wartość z info.plist lub ustaw ją NObez usuwania. Jeśli nie jest dostępny w Twojej info.plist, nie rób nic. Wartość domyślna jest NOdla tej właściwości.

Dodaj poniższy kod do swojego AppDelegate.m:

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

2) Różne ustawienia dla różnych kontrolerów widoku:

Dodaj UIViewControllerBasedStatusBarAppearanceparę klucz-wartość do swojej listy info.plist i ustaw ją na YES.

Jeśli kontroler widoku nie jest osadzony w kontrolerze nawigacji. Powiedzmy, że MyViewController. po prostu dodaj poniższy kod do pliku MyViewController.m. Jeśli kontroler widoku jest osadzony w kontrolerze nawigacji, utwórz nową klasę Cocoa Touch i ustaw ją podklasę UINavigationController. Powiedzmy, że MyNC. Wybierz widok kontrolera nawigacji w swoim Storyboard, w prawym okienku; Narzędzia -> Inspektor tożsamości -> Klasa niestandardowa -> Klasa, wpisz „MyNC”. Po połączeniu widoku Storyboard z klasą Cocoa Touch „MyNC” dodaj poniższy kod do swojego MyNC.m:

- (BOOL)prefersStatusBarHidden {
    return NO;
}

-(UIStatusBarStyle)preferredStatusBarStyle {
    return UIStatusBarStyleLightContent;
}
Fatih Aksu
źródło
Wygląda na to, że w iOS9 UIViewControllerBasedStatusBarAppearance domyślnie zawiera wartość TAK, ponieważ musiałem dodać ją ręcznie w .plist i ustawić na NIE, aby działał poprawnie.
Mohd Asim
4

Mimo wszystkich odpowiedzi tutaj nadal nie znalazłem dla mnie dokładnego rozwiązania, ale zacząłem od odpowiedzi od Daniela. Skończyło się na:

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

w kontrolerach nawigacji (podobnie dla zakładki, tylko selectedViewController). A następnie uszanuje:

override var preferredStatusBarStyle: UIStatusBarStyle {
     return .lightContent
}

W każdym kontrolerze widoku, chyba że ustawisz inaczej. Nie muszę setNeedsStatusBarAppearanceUpdate()nigdzie dzwonić , po prostu aktualizuje się, gdy dotrzesz do każdego kontrolera widoku.

Andrew Plummer
źródło
2
Skończyło się na prawie identycznym rozwiązaniu po godzinach zmagań z tym.
Scott Jungwirth
W pewnym momencie wydaje się, że zostało to naprawione, po prostu używanie preferowanegoStatusBarStyle w każdym VC działa teraz dobrze.
Andrew Plummer
4

Rozwiązanie (a) iOS 13

W odpowiedzi, która uzyskała najwyższą liczbę głosów, użyto „starszego” kodu 👎

Ustawienie tej barStylewłaściwości jest teraz (iOS 13+) uważane za „starsze dostosowanie”. Według Apple ,

W systemie iOS 13 i nowszych dostosuj pasek nawigacji, używając właściwości standardAppearance, compactAppearance i scrollEdgeAppearance. Możesz nadal używać tych starszych akcesoriów do bezpośredniego dostosowywania wyglądu paska nawigacji, ale musisz samodzielnie zaktualizować wygląd dla różnych konfiguracji paska.

Odnośnie twojej próby - byłeś na dobrej drodze!

UINavigationControllerjest podklasą UIViewController(kto wiedział 🙃)!

Dlatego prezentując kontrolery widoku osadzone w kontrolerach nawigacji, tak naprawdę nie prezentujesz osadzonych kontrolerów widoku; prezentujesz kontrolery nawigacji! UINavigationController, jako podklasa UIViewController, dziedziczy preferredStatusBarStylei childForStatusBarStyle, które można ustawić według potrzeb.

Każda z następujących metod powinna działać:

  1. Zastąp preferredStatusBarStylew ramachUINavigationController

    • preferredStatusBarStyle( doc ) - preferowany styl paska stanu dla kontrolera widoku
    • Podklasa lub rozszerzenie UINavigationController

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

      LUB

      extension UINavigationController {
          open override var preferredStatusBarStyle: UIStatusBarStyle {
              .lightContent
          }
      }
  2. Zastąp childForStatusBarStylew ramachUINavigationController

    • childForStatusBarStyle( doc ) - wywoływane, gdy system potrzebuje kontrolera widoku do określenia stylu paska stanu
    • Zgodnie z dokumentacją Apple,

      „Jeśli kontroler widoku kontenera wywodzi styl paska stanu z jednego ze swoich kontrolerów podrzędnych, [zastąp tę właściwość] i zwróć ten kontroler widoku podrzędnego. Jeśli zwrócisz wartość nil lub nie zastąpisz tej metody, zostanie użyty styl paska stanu dla self . Jeśli wartość zwracana przez tę metodę ulegnie zmianie, wywołaj metodę setNeedsStatusBarAppearanceUpdate (). "

    • Innymi słowy, jeśli nie zaimplementujesz tutaj rozwiązania 3, system powróci do rozwiązania 2 powyżej.
    • Podklasa lub rozszerzenie UINavigationController

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

      LUB

      extension UINavigationController {    
          open override var childForStatusBarStyle: UIViewController? {
              topViewController
          }
      }
    • Możesz zwrócić dowolny kontroler widoku, który chcesz powyżej. Polecam jedno z poniższych:

      • topViewController(z UINavigationController) ( doc ) - kontroler widoku u góry stosu nawigacji
      • visibleViewController(z UINavigationController) ( doc ) - kontroler widoku powiązany z aktualnie widocznym widokiem w interfejsie nawigacji (wskazówka: może to obejmować „kontroler widoku przedstawiony modalnie na górze samego kontrolera nawigacji”)

Uwaga: Jeśli zdecydujesz się na podklasę UINavigationController, pamiętaj, aby zastosować tę klasę do swoich kontrolerów nawigacji za pośrednictwem inspektora tożsamości w IB.

PS Mój kod używa składni Swift 5.1 😎

Andrew Kirna
źródło
1
Bardzo pełna odpowiedź, dzięki! Ponadto, coś, z czym walczyłem przez jakiś czas, na iOS 13 .defaultstyl uwzględnia tryb ciemny i nie jest to udokumentowane, więc jeśli obsługujesz również poprzednie wersje iOS, możesz dodać, if #available(iOS 13, *) { return .darkContent } else { return .default }jeśli próbujesz ręcznie ustawić styl paska stanu zgodnie z kolor za paskiem stanu i ten kolor jest „jasny”.
valcanaia
1
Zwróć uwagę, że metoda rozszerzenia polegająca na zastępowaniu zmiennej nie działa już w Xcode 11.4 / iOS 13.4
Marc Etcheverry
Ponieważ rozszerzanie obiektywnych klas C w języku Swift jest implementowane za pomocą kategorii celu C. Nadpisywanie metod z kategorii celu C nie jest zalecane i prawdopodobnie się zepsuje. Zobacz stackoverflow.com/a/38274660/2438634
Marc Etcheverry
Chociaż zastępowanie UINavigationController zdecydowanie działa, wygląda na błąd po stronie Apple, że nie wykonuje on domyślnie childForStatusBarStyle, zwracając jego topViewController. Np. UITabBarController robi to dla swoich kart. Dla mnie nie ma powodu, dla którego UINavigationController, będąc ściśle kontrolerem kontenera do hostowania „prawdziwych” kontrolerów widoku, zamiast prezentować własny interfejs użytkownika, miałby „zjadać” te style paska stanu. Stworzy radar dla Apple.
Igor Vasilev
1

Jeśli chcesz ukryć pasek statusu podczas splashScreen, ale chciałeś zmienić styl na jasną zawartość (StatusBarInitialHidden na Plist musi mieć wartość NO, aby ukryć pasek statusu na powitaniu), możesz dodać to do metody didFinishLaunchingWithOptions aplikacji appDelegate, aby zmienić na lightContent.

[[UIApplication sharedApplication]setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
[[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleLightContent];
aalesano
źródło
1

szybki przykład

w AppDelegate.swift

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

    return true
}

w zestawie info.plist Wyświetl wygląd paska stanu kontrolera: NIE

fyalavuz
źródło
1

Jeśli używasz NavigationController, możesz podklasę, NavigationControlleraby skonsultowała się ze swoim podrzędnym kontrolerem widoku

// 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
źródło
1

Swift 4.2

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

extension UINavigationController {
    open override var childForStatusBarStyle: UIViewController? {
        return visibleViewController
    }
}
Wiaczesław
źródło
Zwróć uwagę, że metoda rozszerzenia polegająca na zastępowaniu zmiennej nie działa już w Xcode 11.4 / iOS 13.4
Marc Etcheverry
@MarcEtcheverry, więc dlaczego odrzuciłeś odpowiedź? wydaje się dziwne.
Wiaczesław
Ponieważ rozszerzanie obiektywnych klas C w języku Swift jest implementowane za pomocą kategorii celu C. Nadpisywanie metod z kategorii celu C nie jest zalecane i prawdopodobnie się zepsuje. Zobacz stackoverflow.com/a/38274660/2438634
Marc Etcheverry
@MarcEtcheverry 'nie zalecane'! = 'Nigdy go nie używaj!'. na lipiec 2018 odpowiedź była prawidłowa. Nawet ta odpowiedź nie jest aktualna, nie jest to powód, aby ją negatywnie oceniać. Nie widzę przyszłości
Wiaczesław
0

Możesz ustawić styl paska stanu. Będzie przypominał pasek stanu, taki jak IOS 6 i poniżej.
Wklej te metody w kontrolerze widoku

-(UIStatusBarStyle)preferredStatusBarStyle{
    return UIStatusBarStyleBlackOpaque;
}

i wywołanie tej metody z widoku ładowało się w ten sposób

if([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f)
    {
       [self setNeedsStatusBarAppearanceUpdate];
    }
Ganapathy
źródło
Masz na myśli [self setStatusBarNeedsUpdate]w drugim bloku? (Lub przynajmniej coś innego).
mxcl
0

Chcę tylko dodać notatkę dotyczącą konkretnego przypadku, z którym miałem do czynienia. Miałem inne okno UIWindow w mojej aplikacji, które wyświetlało czat, który cały czas unosił się po mojej aplikacji. To spowodowało, że żadne z powyższych rozwiązań nie zadziałało i nie jestem pewien, dlaczego! Jedyne, co zauważyłem, to to, że mój ViewController w nowym UIWindow był tego przyczyną! A gdybym chciał zmienić styl paska stanu, muszę to zrobić w tym kontrolerze widoku nowego UIWindow.

Ta uwaga może pomóc innym, którzy mają podobną strukturę! Więc w zasadzie możesz zastosować rozwiązania wymienione powyżej w ViewController nowego UIWindow.

Znowu to konkretny przypadek.

Dzięki

Ehab Saifan
źródło
-1

Dla Swift 3 w Twoim UIViewController:

override var preferredStatusBarStyle : UIStatusBarStyle { return UIStatusBarStyle.lightContent }
Mavrick Laakso
źródło