在不更改状态栏颜色的情况下呈现视图控制器,例如 UIAlertController

Presenting a view controller without changing the status bar color, like UIAlertController

在执行一些网络操作时,我提供了一个模态视图控制器(类似于 MBProgressHUD 但作为视图控制器)以防止用户交互并指示进度。

视图控制器具有 modalPresentationStyle = .Custom 并使用转换委托和自定义呈现控制器进行动画处理。除了动画转换之外,他们没有驱动演示的自定义操作。

我遇到的问题是,无论何时显示视图控制器,它都会将状态栏颜色变为黑色。我可以覆盖 preferredStatusBarStyle 以使其始终 return .LightContent 但有时此视图控制器会显示在带有 .Default 的视图控制器上,我也不想在那里更改它.基本上,我希望具有与 UIAlertController.

相同的行为

我已经尝试配置呈现控制器以将呈现的视图控制器移出状态栏space:

private class SEUIProgressControllerPresentationController: UIPresentationController {

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

    private override func frameOfPresentedViewInContainerView() -> CGRect {
        return super.frameOfPresentedViewInContainerView().insetBy(dx: 40, dy: 100)
    }

    ...
}

这些设置确实将当前控制器的顶部移出状态栏,但状态栏仍然受到影响。是否有 属性 我遗漏的会阻止我的视图控制器更新状态栏样式的东西?

更新

看起来这已在 iOS 10 中修复。默认行为是忽略来自呈现的视图控制器的状态栏规则 除非 呈现的视图控制器有 modalPresentationCapturesStatusBarAppearance == true 或者您使用延伸到状态栏 space(不是 .custom)的几个 built-in 显示控制器之一。

基本上,自定义行为已更改为默认 opt-out 而不是强制 opt-in。


iOS 9.x 及以下

经过大量挖掘,设置应用程序状态栏颜色的内部逻辑如下所示:

var viewController = window.rootViewController!

while let presentedViewController = viewController.valueForKey("_presentedStatusBarViewController") as? UIViewController {
    viewController = presentedViewController
}

while let childViewController = viewController.childViewControllerForStatusBarStyle() {
    viewController = childViewController
}

let style = viewController.preferredStatusBarStyle()

视图控制器的 属性 _presentedStatusBarViewController 是在呈现期间根据其呈现控制器的私有方法 _shouldChangeStatusBarViewController() 的值分配的。此方法的默认实现是 return true,_UIAlertControllerPresentationController 和少数其他表示控制器 return 为 false。

这意味着不更改状态栏的最直接方法就是将此方法添加到我的演示控制器:

private class SEUIProgressControllerPresentationController: UIPresentationController {

    @objc func _shouldChangeStatusBarViewController() -> Bool {
        return false
    }

    ...
}

很遗憾,这无法通过 App Store 审核。

相反,我正在做的是在我的视图控制器中重新创建将应用于呈现视图控制器的逻辑:

public class SEUIProgressController: UIViewController {

    ...
    public override func preferredStatusBarStyle() -> UIStatusBarStyle {

        guard var targetViewController = presentingViewController else {
            return .LightContent
        }

        while let parentViewController = targetViewController.parentViewController {
            targetViewController = parentViewController
        }

        while let childViewController = targetViewController.childViewControllerForStatusBarStyle() {
            targetViewController = childViewController
        }

        return targetViewController.preferredStatusBarStyle()
    }

    public override func prefersStatusBarHidden() -> Bool {

        guard var targetViewController = presentingViewController else {
            return false
        }

        while let parentViewController = targetViewController.parentViewController {
            targetViewController = parentViewController
        }

        while let childViewController = targetViewController.childViewControllerForStatusBarHidden() {
            targetViewController = childViewController
        }

        return targetViewController.prefersStatusBarHidden()
    }
}