Wie ändere ich die Hintergrundfarbe und die Textfarbe der Statusleiste unter iOS 13?

72

Mit der Ankunft von iOS 13 ist die Ansicht von statusBar nicht mehr über Folgendes zugänglich:

value(forKey: "statusBar") as? UIView

Durch:

Beenden der App aufgrund der nicht erfassten Ausnahme 'NSInternalInconsistencyException', Grund: 'App mit dem Namen -statusBar oder -statusBarWindow bei UIApplication: Dieser Code muss geändert werden, da keine Statusleiste oder kein Statusleistenfenster mehr vorhanden ist. Verwenden Sie stattdessen das statusBarManager-Objekt in der Fensterszene. '

Es ist jedoch nicht klar, wie es zum Ändern von Farben verwendet werden soll, da keyWindow?.windowScene?.statusBarManageres anscheinend nichts damit zu tun hat.

Ich kompiliere meinen Code mit (iOS 10, *) Kompatibilität, daher beabsichtige ich, UIKit weiterhin zu verwenden.

Irgendwelche Ideen zu diesem Thema?

Hugo Alonso
quelle
3
Warum versuchen Sie, die Hintergrundfarbe der Statusleiste manuell zu ändern? Standardmäßig entspricht es der Farbe Ihrer App.
rmaddy
Es ist eine Legacy-App, die eine benutzerdefinierte Statusleistenfarbe enthält, die sie vom Rest der App abhebt
Hugo Alonso
5
Es gab noch nie eine gültige Möglichkeit, die Farbe der Statusleiste zu ändern. Solche Lösungen brechen immer irgendwann. Niemals in die private Subview-Struktur eintauchen.
rmaddy
@ HugoAlonso sehen meine Antwort hier: stackoverflow.com/questions/56556254/…
Mike
@rmaddy Kannst du eine Antwort hinzufügen, die besagt, dass dies nicht möglich ist und was der beste Ansatz ist, damit ich ihn als akzeptierten festlegen kann?
Hugo Alonso

Antworten:

48

Sie können einige Bedingungen hinzufügen oder die erste verwenden. Erstellen Sie einfach eine Erweiterung für UIApplication.

extension UIApplication {
var statusBarUIView: UIView? {
    if #available(iOS 13.0, *) {
        let tag = 38482
        let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first

        if let statusBar = keyWindow?.viewWithTag(tag) {
            return statusBar
        } else {
            guard let statusBarFrame = keyWindow?.windowScene?.statusBarManager?.statusBarFrame else { return nil }
            let statusBarView = UIView(frame: statusBarFrame)
            statusBarView.tag = tag
            keyWindow?.addSubview(statusBarView)
            return statusBarView
        }
    } else if responds(to: Selector(("statusBar"))) {
        return value(forKey: "statusBar") as? UIView
    } else {
        return nil
    }
  }
}

AKTUALISIERT: Entschuldigung, ich habe nicht genug Zeit, um es in realen Projekten zu testen, aber es funktioniert in der App "Hallo Welt". Sie können weitere Informationen zu keyWindow und statusBarFrame lesen , um es besser zu machen.

extension UIApplication {
var statusBarUIView: UIView? {

    if #available(iOS 13.0, *) {
        let tag = 3848245

        let keyWindow = UIApplication.shared.connectedScenes
            .map({$0 as? UIWindowScene})
            .compactMap({$0})
            .first?.windows.first

        if let statusBar = keyWindow?.viewWithTag(tag) {
            return statusBar
        } else {
            let height = keyWindow?.windowScene?.statusBarManager?.statusBarFrame ?? .zero
            let statusBarView = UIView(frame: height)
            statusBarView.tag = tag
            statusBarView.layer.zPosition = 999999

            keyWindow?.addSubview(statusBarView)
            return statusBarView
        }

    } else {

        if responds(to: Selector(("statusBar"))) {
            return value(forKey: "statusBar") as? UIView
        }
    }
    return nil
  }
}
ist versteckt
quelle
4
keyWindowist in iOS 13 veraltet.
rmaddy
1
Warum sollten Sie etwas verwenden, das veraltet ist?
Hugo Alonso
@isHidden Ich habe die iOS-Version mit np verwendet, auf Xcode 11 und iOS 13 aktualisiert und bin abgestürzt. Ich habe den Erweiterungscode auf Ihre Antwort überprüft und keine Abstürze mehr, danke. Aber können Sie ein Beispiel geben, wie Sie Ihre Code-Informationen iOS 12 und iOS 13 verwenden. Im Moment zeigt es nur, was in der Erweiterung hinzugefügt werden soll
Lance Samaria
1
wie von @rmaddy gezeigt, keyWindowsowie statusBarFramebeide veraltet sind
Preetam
neu bei iOS dev. Kann mir jemand sagen, wo ich dieses Code-Snippet verwenden soll? in AppDelegate oder viewController?
MMK
24

Leider hat Apple einige der genannten Methoden für den Zugriff auf die Statusleiste und die Bearbeitung ihrer Attribute abgelehnt. Sie müssen das StatusBarManagerObjekt der verwenden WindowScene. Die folgende Methode funktioniert für iOS 13 und höher:

extension UINavigationController {

    func setStatusBar(backgroundColor: UIColor) {
        let statusBarFrame: CGRect
        if #available(iOS 13.0, *) {
            statusBarFrame = view.window?.windowScene?.statusBarManager?.statusBarFrame ?? CGRect.zero
        } else {
            statusBarFrame = UIApplication.shared.statusBarFrame
        }
        let statusBarView = UIView(frame: statusBarFrame)
        statusBarView.backgroundColor = backgroundColor
        view.addSubview(statusBarView)
    }

}
Alaa Othman
quelle
2
Beste Antwort von allen
Patrick
1
Perfekte Lösung!
Vinayak Bhor
Wie wäre es mit der Überprüfung, ob die StatusBar-Ansicht bereits hinzugefügt wurde? Dies soll sicherstellen, dass wir nicht immer wieder dieselbe Unteransicht hinzufügen.
DB
Das Problem bei dieser Lösung ist, dass statusBarFrame null ist, falls Sie es in viewDidLoad oder viewWillAppear aufrufen. Funktioniert nur in viewDidAppear, was nicht als der beste Ort zum Einrichten der Benutzeroberfläche
klingt
Wenn Sie sich nicht sicher sind, wie Sie das so nennen sollen, rufen Sie in viewDidAppear (danke @Artiom) self.navigationController? .StetStatusBar (backgroundColor: yourColor) UND self.navigationController? .NavigationBar.setNeedsLayout ()
Ben
8

Ich bin auf dieses Problem schon einmal gestoßen. Meine Anwendung stürzte ab, während ich diesen Code mit XCode 11 und Swift 5.0 ausführte.

Vorheriger Code: -

UIApplication.shared.statusBarView?.backgroundColor = UIColor.init(red: 243/250, green: 243/250, blue: 243/250, alpha: 1)

Nur geändert zu: -

if #available(iOS 13.0, *) {
    let statusBar = UIView(frame: UIApplication.shared.keyWindow?.windowScene?.statusBarManager?.statusBarFrame ?? CGRect.zero)
     statusBar.backgroundColor = UIColor.init(red: 243/250, green: 243/250, blue: 243/250, alpha: 1)
     UIApplication.shared.keyWindow?.addSubview(statusBar)
} else {
     UIApplication.shared.statusBarView?.backgroundColor = UIColor.init(red: 243/250, green: 243/250, blue: 243/250, alpha: 1)
}

Jetzt ist mein Problem gelöst. Viel Spaß beim Codieren.

Ajeet
quelle
4
keyWindowist in iOS 13 veraltet.
rmaddy
let keyWindow = UIApplication.shared.connectedScenes .filter ({$ 0.activationState == .foregroundActive}) .map ({$ 0 als? UIWindowScene}) .compactMap ({$ 0}) .first? .windows .filter ({$ 0.isKeyWindow }). first Sie können direkt anrufen als: - keyWindow? .addSubview (statusBar)
Ajeet
Fügen Sie keinen Code in einen Kommentar ein, aktualisieren Sie Ihre Antwort nach Bedarf. Denken Sie daran, dass es möglicherweise zwei aktive Szenen gibt, sodass es falsch sein kann, einfach die erste zu greifen.
rmaddy
Für keyWindows: UIApplication.shared.windows.filter {$ 0.isKeyWindow} .first
Roman
6

Das hat bei mir in Swift 5 funktioniert

   override func viewDidLoad() {
      super.viewDidLoad()

      if #available(iOS 13, *)
      {
          let statusBar = UIView(frame: (UIApplication.shared.keyWindow?.windowScene?.statusBarManager?.statusBarFrame)!)
          statusBar.backgroundColor = #colorLiteral(red: 0.2346, green: 0.3456, blue: 0.5677, alpha: 1)
          UIApplication.shared.keyWindow?.addSubview(statusBar)
      } else {
         // ADD THE STATUS BAR AND SET A CUSTOM COLOR
         let statusBar: UIView = UIApplication.shared.value(forKey: "statusBar") as! UIView
         if statusBar.responds(to:#selector(setter: UIView.backgroundColor)) {
            statusBar.backgroundColor = #colorLiteral(red: 0.2346, green: 0.3456, blue: 0.5677, alpha: 1)
         }
         UIApplication.shared.statusBarStyle = .lightContent
      }
   }
C13
quelle
5

Verwenden Sie den folgenden Code:

if (@available(iOS 13, *)) {
    let statusBar1 =  UIView()
    statusBar1.frame = UIApplication.shared.statusBarFrame
    statusBar1.backgroundColor = UIColor.red
    UIApplication.shared.keyWindow?.addSubview(statusBar1)
}

um dieses Ergebnis zu erzielen:

Geben Sie hier die Bildbeschreibung ein

Tajinder singh
quelle
'statusBarFrame' war in iOS 13.0 veraltet: Verwenden Sie stattdessen die Eigenschaft statusBarManager der Fensterszene. 'keyWindow' war in iOS 13.0 veraltet: Sollte nicht für Anwendungen verwendet werden, die mehrere Szenen unterstützen, da ein Schlüsselfenster für alle verbundenen Szenen zurückgegeben wird ...
Giuseppe Garassino
3
if (@available(iOS 13, *))
{    
    UIView *statusBar = [[UIView alloc]initWithFrame:[UIApplication sharedApplication].keyWindow.windowScene.statusBarManager.statusBarFrame] ;
    statusBar.backgroundColor = [UIColor redColor];
    [[UIApplication sharedApplication].keyWindow addSubview:statusBar];

 }
BASIL BABY
quelle
2
keyWindowist in iOS 13 veraltet.
rmaddy
3

Getestet und 100% für mich gearbeitet

func statusBarColorChange(){

    if #available(iOS 13.0, *) {

            let statusBar = UIView(frame: UIApplication.shared.keyWindow?.windowScene?.statusBarManager?.statusBarFrame ?? CGRect.zero)
            statusBar.backgroundColor = AppThemeColor
            statusBar.tag = 100
            UIApplication.shared.keyWindow?.addSubview(statusBar)

    } else {

            let statusBar = UIApplication.shared.value(forKeyPath: "statusBarWindow.statusBar") as? UIView
            statusBar?.backgroundColor = AppThemeColor

        }
    }
}

Entfernen Sie die Statusleiste aus dem Schlüsselfenster

func removeStatusBar(){

    if #available(iOS 13.0, *) {

        UIApplication.shared.keyWindow?.viewWithTag(100)?.removeFromSuperview()

    }
}

in viewDidLoadund viewWillAppearrufen Sie die obige Funktion auf

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    removeStatusBar()
    statusBarColorChange()
}

override func viewDidLoad() {
    super.viewDidLoad()

    removeStatusBar()
    statusBarColorChange()

}
Milan Aghera
quelle
3

für swift 5.0 habe ich dies getan, um die Hintergrundfarbe zu ändern,

    if #available(iOS 13.0, *) {
           let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first 
           // Reference - https://stackoverflow.com/a/57899013/7316675
           let statusBar = UIView(frame: window?.windowScene?.statusBarManager?.statusBarFrame ?? CGRect.zero)
           statusBar.backgroundColor = .white
           window?.addSubview(statusBar)
    } else {
           UIApplication.shared.statusBarView?.backgroundColor = .white
           UIApplication.shared.statusBarStyle = .lightContent
    }

https://medium.com/@trivediniki94/surprises-after-upgrading-to-xcode-11-ios-13-b52b36e05fa8

Niki
quelle
Das ist besser, aber es ist immer noch nicht ideal. Was passiert, wenn die App zwei verschiedene Szenen auf einem iPad zeigt? Dieser Code zeigt nur die zusätzliche Ansicht über einer der Szenen und nicht beide.
rmaddy
@rmaddy Also sollte ich eine for-Schleife setzen und welche würde den gleichen Code für alle Fenster ausführen?
Niki
1
Gibt es eine Chance, dass jemand eine Objective C-Version davon bereitstellt?
Steve Brooker
Der Wert vom Typ '[UIWindow]' hat kein Mitglied 'addSubview'
Marlhex
@Daniella Ich habe meinen Code aktualisiert. Bitte überprüfen Sie Ihren Code jetzt!
Niki
2

Sie können dies versuchen

if (@available(iOS 13, *))
{
    UIView *_localStatusBar = [[UIApplication sharedApplication].keyWindow.windowScene.statusBarManager performSelector:@selector(createLocalStatusBar)];
    statusBar = [_localStatusBar performSelector:@selector(statusBar)];
}
else
{
    statusBar = [[UIApplication sharedApplication] valueForKey:@"statusBar"];
}
Ewane Shen
quelle
3
keyWindowist in iOS 13 veraltet.
rmaddy
1
Können Sie bitte Methoden hinzufügen - createLocalStatusBar und @selector (statusBar)
Ram G.
1

KeyWindow ist in iOS13 veraltet. Sie können diese Erweiterung für Swift 5 und iOS 13 nach oben verwenden

extension UIApplication {

    var statusBarUIView: UIView? {

        if #available(iOS 13.0, *) {
            let tag = 3848245

            let keyWindow: UIWindow? = UIApplication.shared.windows.filter {$0.isKeyWindow}.first

            if let statusBar = keyWindow?.viewWithTag(tag) {
                return statusBar
            } else {
                let height = keyWindow?.windowScene?.statusBarManager?.statusBarFrame ?? .zero
                let statusBarView = UIView(frame: height)
                statusBarView.tag = tag
                statusBarView.layer.zPosition = 999999

                keyWindow?.addSubview(statusBarView)
                return statusBarView
            }

        } else {

            if responds(to: Selector(("statusBar"))) {
                return value(forKey: "statusBar") as? UIView
            }
        }
        return nil
      }
}

Und verwenden Sie es in Ihrer didFinishLaunchingWithOptions in der AppDelegate- Klasse wie folgt :

UIApplication.shared.statusBarUIView?.backgroundColor = .red(any color)
MiddleEastBankDev
quelle
Wie wäre es mit Notch Device?
karthikeyan
Es funktioniert auf allen Arten von Geräten, auch Notch-Geräten.
MiddleEastBankDev
0

Dies ist eine ObjC-Version der am häufigsten gewählten Antwort für diejenigen wie mich, die sie noch verwenden:

Erstellen Sie eine Kategorie von UIApplication und fügen Sie sie Ihrem Projekt hinzu:

@implementation UIApplication (iOS13PorcoDiDio)

- (UIView*) statusBar
{

    if([UIDevice getOSVersion] >= 13)
    {

        const NSUInteger k_TAG_STATUSBAR = 38482458385;

        UIView * vStatusBar = [[UIApplication sharedApplication].keyWindow viewWithTag:k_TAG_STATUSBAR];
        if(vStatusBar != nil)

            return vStatusBar;

        else {

            UIView *vStatusBar = [[UIView alloc] initWithFrame:[UIApplication sharedApplication].statusBarFrame];
            [vStatusBar setTag:k_TAG_STATUSBAR];
            [[UIApplication sharedApplication].keyWindow addSubview:vStatusBar];

            return vStatusBar;

        }

    } else if([UIApplication respondsToSelector:@selector(statusBar)])

        return (UIView*)[UIApplication sharedApplication].statusBar;

    else

        return nil;


}

@end
Fabio Napodano
quelle
1
keyWindowist in iOS 13 veraltet.
rmaddy
2
PorcoDiDioist in iOS 13
Alberto Schiariti
0

Ich denke, der einfachste Weg ist, NavigationController anstelle von ViewController zu verwenden. Das Ändern des Hintergrunds der Navigationsleiste mithilfe des Storyboards wirkt sich auch auf die Statusleiste aus

NavigationController

Shalonteoh
quelle
-1

Ich mache das, kann statusBar bekommen, aber set statusBar backgroundColor funktioniert nicht

UIView *statusBar;
if (@available(iOS 13, *)) {
    UIView *_localStatusBar = [[UIApplication sharedApplication].keyWindow.windowScene.statusBarManager performSelector:@selector(createLocalStatusBar)];
    statusBar = [_localStatusBar performSelector:@selector(statusBar)];
}
else {
    statusBar = [[UIApplication sharedApplication] valueForKey:@"statusBar"];
}
if ([statusBar respondsToSelector:@selector(setBackgroundColor:)]) {
    statusBar.backgroundColor = [UIColor redColor];
}
sandig
quelle
2
keyWindowist in iOS 13 veraltet.
rmaddy
Also, wie bekomme ich statusBarView in OC
sandig
1
Es ist keine statusBarView zu erhalten. Es gibt keine öffentliche API dafür. In iOS 13 können Sie den statusBarManager aus einer Fensterszene abrufen, es gibt jedoch keine Ansicht.
rmaddy
-1

Dies ist die beste Antwort, die ich je gesehen habe. Prost

    if #available(iOS 13.0, *) {
    let app = UIApplication.shared
    let statusBarHeight: CGFloat = app.statusBarFrame.size.height

    let statusbarView = UIView()
        statusbarView.backgroundColor = ColorPalette.grayChateau  
    view.addSubview(statusbarView)

    statusbarView.translatesAutoresizingMaskIntoConstraints = false
    statusbarView.heightAnchor
        .constraint(equalToConstant: statusBarHeight).isActive = true
    statusbarView.widthAnchor
        .constraint(equalTo: view.widthAnchor, multiplier: 1.0).isActive = true
    statusbarView.topAnchor
        .constraint(equalTo: view.topAnchor).isActive = true
    statusbarView.centerXAnchor
        .constraint(equalTo: view.centerXAnchor).isActive = true

} else {
    let statusBar = UIApplication.shared.value(forKeyPath: "statusBarWindow.statusBar") as? UIView
    statusBar?.backgroundColor = UIColor.red
 }
Archu Mohan
quelle
'statusBarFrame' war in iOS 13.0 veraltet: Verwenden Sie stattdessen die Eigenschaft statusBarManager der Fensterszene.
Marlhex