【问题标题】:How to change the status bar background color and text color on iOS 13?如何在 iOS 13 上更改状态栏背景颜色和文本颜色?
【发布时间】:2019-11-01 05:22:37
【问题描述】:

随着 iOS 13 的到来,statusBar 的视图不再可以通过以下方式访问:

value(forKey: "statusBar") as? UIView

由于:

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

但不清楚它应该如何用于更改颜色,因为keyWindow?.windowScene?.statusBarManager 似乎不包含任何相关内容。

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

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

【问题讨论】:

  • 为什么要手动更改状态栏背景颜色?默认情况下,它将与您的应用程序的颜色相匹配。
  • 这是一个旧版应用程序,其中包含自定义状态栏颜色,使其看起来与应用程序的其余部分不同
  • 从来没有一种有效的方法来修改状态栏的颜色。这样的解决方案最终总是会失败。永远不要深入研究私有子视图结构。
  • @HugoAlonso 在这里查看我的答案:stackoverflow.com/questions/56556254/…
  • @rmaddy 您能否添加一个答案,说明不可能的事实以及最好的方法是什么,以便我可以将其设置为接受的方法?

标签: ios swift statusbar ios13


【解决方案1】:

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

【讨论】:

  • keyWindow 在 iOS 13 中已弃用。
  • 为什么要使用已弃用的东西?
  • @isHidden 我正在使用带有 np 的 iOS 版本,更新到 Xcode 11 和 iOS 13 并且发生了崩溃,我检查了您的答案的扩展代码并且不再崩溃,谢谢。但是你能举一个例子来说明如何使用你的代码信息 iOS 12 和 iOS 13。现在它只是显示了在扩展中添加的内容
  • @rmaddy 指出,keyWindowstatusBarFrame 均已弃用
  • iOS 开发新手。谁能告诉我在哪里使用这个代码sn-p?在 AppDelegate 或 viewController 中?
【解决方案2】:

不幸的是,Apple 弃用了上述一些访问状态栏和编辑其属性的方法。您必须使用WindowSceneStatusBarManager 对象。以下方法适用于 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)
    }

}

【讨论】:

  • 最佳答案
  • 完美解决方案!
  • 如何检查 statusBar 视图是否已经添加?这是为了确保我们不会一次又一次地添加相同的子视图。
  • 这个解决方案的问题是statusBarFrame为nil,以防你在viewDidLoad或viewWillAppear中调用它。仅适用于 viewDidAppear 听起来不是设置 UI 的最佳位置
  • 如果您不确定如何像我一样调用它,请在 viewDidAppear(感谢@Artiom)中调用 self.navigationController?.setStatusBar(backgroundColor: yourColor) AND self.navigationController?.navigationBar.setNeedsLayout()
【解决方案3】:

我以前遇到过这个问题。当我使用 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)
}

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

【讨论】:

  • keyWindow 在 iOS 13 中已弃用。
  • 让 keyWindow = UIApplication.shared.connectedScenes .filter({$0.activationState == .foregroundActive}) .map({$0 as? UIWindowScene}) .compactMap({$0}) .first?. windows .filter({$0.isKeyWindow}).first 可以直接调用为:- keyWindow?.addSubview(statusBar)
  • 不要将代码放在评论中,根据需要更新您的答案。请记住,可能有两个活动场景,所以简单地抓住第一个可能是错误的。
  • 对于 keyWindows:UIApplication.shared.windows.filter {$0.isKeyWindow}.first
【解决方案4】:

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

【讨论】:

  • 我收到一个警告:iOS 13.0 中不推荐使用“keyWindow”:不应该用于支持多个场景的应用程序,因为它会在所有连接的场景中返回一个关键窗口
【解决方案5】:

对于 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 上显示两个不同的场景时会发生什么?此代码仅显示其中一个场景上方的额外视图,而不是两者。
  • @rmaddy 那么我应该放一个for循环,它会为所有窗口执行相同的代码吗?
  • 有人提供这个的 Objective C 版本的机会吗?
  • “[UIWindow]”类型的值没有成员“addSubview”
  • @Daniella 我已经更新了我的代码,请您现在检查您的代码!
【解决方案6】:

经过测试,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()

}

【讨论】:

    【解决方案7】:

    使用以下代码:

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

    要达到这个结果:

    【讨论】:

    • 'statusBarFrame' 在 iOS 13.0 中已弃用:改用窗口场景的 statusBarManager 属性。 'keyWindow' 在 iOS 13.0 中已弃用:不应用于支持多个场景的应用程序,因为它会在所有连接的场景中返回一个关键窗口...
    【解决方案8】:
    if (@available(iOS 13, *))
    {    
        UIView *statusBar = [[UIView alloc]initWithFrame:[UIApplication sharedApplication].keyWindow.windowScene.statusBarManager.statusBarFrame] ;
        statusBar.backgroundColor = [UIColor redColor];
        [[UIApplication sharedApplication].keyWindow addSubview:statusBar];
    
     }
    

    【讨论】:

    • keyWindow 在 iOS 13 中已弃用。
    【解决方案9】:

    KeyWindow 在 iOS13 中已弃用。 您可以将此 extension 用于 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)
    

    【讨论】:

    • 缺口设备怎么样?
    • 它适用于所有类型的设备,甚至是缺口设备。
    • 这不起作用。
    【解决方案10】:

    你可以试试这个

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

    【讨论】:

    • keyWindow 在 iOS 13 中已弃用。
    • 能否请您添加方法 - createLocalStatusBar 和 @selector(statusBar)
    【解决方案11】:

    对于像我这样仍在使用它的人来说,这是投票最多的答案的 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
    

    【讨论】:

    • keyWindow 在 iOS 13 中已弃用。
    • PorcoDiDio 在 iOS 13 中已弃用
    【解决方案12】:

    这是我见过的最好的答案.. 干杯

        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
     }
    

    【讨论】:

    • 'statusBarFrame' 在 iOS 13.0 中已弃用:改用窗口场景的 statusBarManager 属性。
    【解决方案13】:

    我认为最简单的方法是使用 NavigationController 而不是 ViewController。使用故事板更改导航栏背景也将反映在状态栏上

    【讨论】:

    • 很高兴看到属性检查器中的哪些设置被更改了,因为当我在 NavigationController 中更改标题背景时,它不会影响状态栏背景
    【解决方案14】:
    Use this Extension:
    
    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)
    }}
    

    【讨论】:

      【解决方案15】:

      我这样做,可以获得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];
      }
      

      【讨论】:

      • keyWindow 在 iOS 13 中已弃用。
      • 那么,如何在OC中获取statusBarView
      • 没有statusBarView可以获取。它没有公共 API。在 iOS 13 中,您可以从窗口场景中获取 statusBarManager,但没有视图。
      猜你喜欢
      • 2013-10-04
      • 2016-10-16
      • 2020-01-26
      • 1970-01-01
      • 2015-09-26
      • 1970-01-01
      • 1970-01-01
      • 2018-04-10
      • 2022-12-14
      相关资源
      最近更新 更多