如何在iOS 13上更改状态栏的背景颜色和文本颜色?


72

随着iOS 13的到来,statusBar的视图不再能通过低谷访问:

value(forKey: "statusBar") as? UIView

由于:

由于未捕获的异常“ NSInternalInconsistencyException”而终止应用程序,原因:“在UIApplication上名为-statusBar或-statusBarWindow的应用程序:此代码必须更改,因为不再有状态栏或状态栏窗口。而是在窗口场景上使用statusBarManager对象。”

但是尚不清楚应如何使用它来更改颜色,因为keyWindow?.windowScene?.statusBarManager它似乎不包含与之相关的任何内容。

我正在编译具有(iOS 10,*)兼容性的代码,因此我打算继续使用UIKit。

关于这个主题有什么想法吗?


3
为什么要尝试手动更改状态栏的背景色?默认情况下,它将与您的应用程序的颜色匹配。
rmaddy19

这是一个旧版应用程序,其中包含自定义状态栏颜色,使其看起来与应用程序的其余部分不同
Hugo Alonso

5
从来没有一种有效的方法来修改状态栏颜色。这样的解决方案总是最终被打破。切勿深入探讨私有子视图结构。
rmaddy19年

@HugoAlonso在这里看到我的答案:stackoverflow.com/questions/56556254/…–
Mike

@rmaddy您能否添加一个答案,说明不可能的事实,什么是最好的方法,所以我可以将其设置为可接受的方法?
雨果·阿隆索

Answers:


48

您可以添加一些条件或使用第一个条件。只需为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
    }
  }
}

更新:抱歉,我没有足够的时间在实际项目中对其进行测试,但是它可以在“ Hello world”应用程序中使用。您可以阅读有关keyWindowstatusBarFrame的更多信息,以使其更好。

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

4
keyWindow在iOS 13中已弃用
。– rmaddy

1
您为什么要使用不推荐使用的东西?
雨果·阿隆索

@isHidden我使用的是带有np的iOS版本,已更新为Xcode 11和iOS 13并发生崩溃,我检查了扩展代码以解决您的问题,不再崩溃,谢谢。但是,您能举一个有关如何使用代码信息iOS 12和iOS 13的示例吗?现在,它仅显示扩展中要添加的内容
Lance Samaria

1
正如@rmaddy所指出的,keyWindow以及statusBarFrame两者均已弃用
preetam '19

iOS开发人员的新手。谁能告诉我在哪里使用此代码段?在AppDelegate或viewController吗?
MMK

24

不幸的是,Apple不赞成使用某些提及的访问状态栏和编辑其属性的方法。您将必须使用的StatusBarManager对象WindowScene。以下方法适用于iOS 13及更高版本:

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

}

2
最好的答案
Patrick

1
完美的解决方案!
Vinayak Bhor

如何检查statusBar视图是否已添加?这是为了确保我们不会一次又一次地添加相同的子视图。
DB

此解决方案的问题在于,如果您在viewDidLoad或viewWillAppear中调用statusBarFrame,则此状态为nil。仅在viewDidAppear中起作用,这听起来不是设置UI的最佳位置
Artiom Lemiasheuski

如果您不确定如何调用这个,好像我是在viewDidAppear(感谢@Artiom)调用self.navigationController .setStatusBar?(的backgroundColor:yourColor)和self.navigationController .navigationBar.setNeedsLayout()

8

我以前遇到过这个问题。当我使用XCode 11和Swift 5.0运行此代码时,我的应用程序崩溃了。

先前的代码:

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

刚刚更改为:-

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

现在我的问题解决了。快乐的编码。


4
keyWindow在iOS的13已经过时
rmaddy

让keyWindow = UIApplication.shared.connectedScenes .filter({$ 0.activationState == .foregroundActive}).map({$ 0 as?UIWindowScene}).compactMap({$ 0}).first?.windows .filter({$ 0.isKeyWindow })首先,你可以直接调用为: - keyWindow .addSubview(状态栏。)?
Ajeet

不要在注释中添加代码,请根据需要更新答案。请记住,可能有两个活动场景,所以简单地抓住第一个场景可能是错误的。
rmaddy19 '19

对于keyWindows:UIApplication.shared.windows.filter {$ 0.isKeyWindow} .first
罗马

6

这在Swift 5中对我有用

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

5

使用以下代码:

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

为了达到这个结果:

在此处输入图片说明


在iOS 13.0中不建议使用“ statusBarFrame”:而是使用窗口场景的statusBarManager属性。iOS 13.0中已弃用'keyWindow':不应该用于支持多个场景的应用程序,因为它会返回所有已连接场景的关键窗口...
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];

 }

2
keyWindow在iOS 13中已弃用
。– rmaddy

3

经过测试,100%为我工作

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

        }
    }
}

从键窗口中删除状态栏

func removeStatusBar(){

    if #available(iOS 13.0, *) {

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

    }
}

viewDidLoadviewWillAppear调用上面的函数

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

    removeStatusBar()
    statusBarColorChange()
}

override func viewDidLoad() {
    super.viewDidLoad()

    removeStatusBar()
    statusBarColorChange()

}

3

对于Swift 5.0,我这样做是为了更改背景颜色,

    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


更好,但是仍然不理想。当应用在iPad上显示两个不同的场景时会发生什么?此代码仅显示一个场景上方的额外视图,而不同时显示两个场景。
rmaddy19,19年

@rmaddy因此,我应该放置一个for循环,并对所有Windows执行相同的代码吗?
Niki

1
有人提供此目标的C版本吗?
史蒂夫·布鲁克

类型'[UIWindow]'的值没有成员'addSubview'–
Marlhex

@Daniella我已经更新了代码,请现在检查您的代码!
尼基,

2

你可以试试这个

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"];
}

3
keyWindow在iOS 13中已弃用
。– rmaddy

1
能否请您添加方法-createLocalStatusBar和@selector(statusBar)
Ram

1

在iOS13中不推荐使用KeyWindow。您可以将此扩展程序用于Swift 5iOS 13至顶部

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

并在AppDelegate类的didFinishLaunchingWithOptions中使用它,例如:

UIApplication.shared.statusBarUIView?.backgroundColor = .red(any color)

缺口装置怎么样?
karthikeyan

它适用于所有类型的设备,甚至包括缺口设备。
MiddleEastBankDev

0

对于像我这样仍在使用它的人来说,这是大多数投票答案的ObjC版本:

创建一个UIApplication类别并将其添加到您的项目中:

@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

1
keyWindow在iOS 13中已弃用
。– rmaddy

2
PorcoDiDio在iOS 13中已弃用
Alberto Schiariti,

0

我认为最简单的方法是使用NavigationController而不是ViewController。使用情节提要更改导航栏背景也会反映在状态栏上

NavigationController


-1

我这样做,可以获取statusBar,但设置statusBar backgroundColor不起作用

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

2
keyWindow在iOS 13中已弃用
。– rmaddy

因此,如何在OC中获取statusBarView
桑迪

1
没有要获取的statusBarView。没有公共API。在iOS 13中,您可以从窗口场景获取statusBarManager,但是没有视图。
rmaddy19年

-1

这是我见过的最好的答案。

    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
 }

在iOS 13.0中不建议使用“ statusBarFrame”:而是使用窗口场景的statusBarManager属性。
Marlhex
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.