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

时间:2022-12-05 15:00:03

When performing some network operations, I present a modal view controller (similar to MBProgressHUD but as a view controller) to prevent user interaction and indicate progress.

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

The view controller has modalPresentationStyle = .Custom and is animated using a transitioning delegate and custom presentation controller. Beyond animating the transitions they have no custom actions driving the presentation.

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

The problem I have is that whenever the view controller is presented, it turns the status bar color black. I could override preferredStatusBarStyle to make it always return .LightContent but sometimes this view controller is presented over a view controller with .Default and I don't want to change it there either. Basically, I want to have the same behavior as UIAlertController.

我遇到的问题是,无论何时出现视图控制器,它都会将状态栏颜色变为黑色。我可以覆盖preferredStatusBarStyle使它总是返回.LightContent但有时这个视图控制器是通过带有.Default的视图控制器呈现的,我也不想在那里更改它。基本上,我想要与UIAlertController具有相同的行为。

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

I have tried configuring the presentation controller to move the presented view controller out of the status bar space:

我已尝试配置演示控制器以将呈现的视图控制器移出状态栏空间:

private class SEUIProgressControllerPresentationController: UIPresentationController {

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

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

    ...
}

These settings do move the top of the presented controller out of the status bar but the status bar is still affected. Is there a property I am missing that would stop my view controller from updating the status bar style?

这些设置会将显示控制器的顶部移出状态栏,但状态栏仍会受到影响。是否有一个我错过的属性会阻止我的视图控制器更新状态栏样式?

1 个解决方案

#1


4  

Update

It looks like this has been fixed in iOS 10. The default behavior is to ignore the status bar rules from the presented view controller unless either the presented view controller has modalPresentationCapturesStatusBarAppearance == true or you use one of several built-in presentation controllers that extend into the status bar space (not .custom).

看起来这已在iOS 10中修复。默认行为是忽略呈现的视图控制器中的状态栏规则,除非呈现的视图控制器具有modalPresentationCapturesStatusBarAppearance == true或您使用扩展的几个内置表示控制器之一进入状态栏空间(不是.custom)。

Basically, the behavior for custom has changed to default opt-out rather than forced opt-in.

基本上,自定义的行为已更改为默认选择退出而不是强制选择加入。


For iOS 9.x and lower

After much digging, the internal logic for setting the application's status bar color looks like this:

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

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

The view controller's property _presentedStatusBarViewController is assigned during presentation based on the value of its presentation controller's private method _shouldChangeStatusBarViewController(). The default implementation of this method is to return true, with _UIAlertControllerPresentationController and a handful of other presentation controllers returning false.

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

That means the most direct way to not change that status bar is simply to add this method to my presentation controller:

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

private class SEUIProgressControllerPresentationController: UIPresentationController {

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

    ...
}

Unfortunately, this won't pass an App Store review.

不幸的是,这不会通过App Store审核。

Instead, what I am doing is recreating the logic that would be applied to the presenting view controller in my view controller:

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

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

#1


4  

Update

It looks like this has been fixed in iOS 10. The default behavior is to ignore the status bar rules from the presented view controller unless either the presented view controller has modalPresentationCapturesStatusBarAppearance == true or you use one of several built-in presentation controllers that extend into the status bar space (not .custom).

看起来这已在iOS 10中修复。默认行为是忽略呈现的视图控制器中的状态栏规则,除非呈现的视图控制器具有modalPresentationCapturesStatusBarAppearance == true或您使用扩展的几个内置表示控制器之一进入状态栏空间(不是.custom)。

Basically, the behavior for custom has changed to default opt-out rather than forced opt-in.

基本上,自定义的行为已更改为默认选择退出而不是强制选择加入。


For iOS 9.x and lower

After much digging, the internal logic for setting the application's status bar color looks like this:

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

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

The view controller's property _presentedStatusBarViewController is assigned during presentation based on the value of its presentation controller's private method _shouldChangeStatusBarViewController(). The default implementation of this method is to return true, with _UIAlertControllerPresentationController and a handful of other presentation controllers returning false.

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

That means the most direct way to not change that status bar is simply to add this method to my presentation controller:

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

private class SEUIProgressControllerPresentationController: UIPresentationController {

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

    ...
}

Unfortunately, this won't pass an App Store review.

不幸的是,这不会通过App Store审核。

Instead, what I am doing is recreating the logic that would be applied to the presenting view controller in my view controller:

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

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