不调用preferredStatusBarStyle

问题描述 投票:234回答:20

我跟着this thread来覆盖-preferredStatusBarStyle,但它没有被调用。我可以更改任何选项以启用它吗? (我在我的项目中使用XIB。)

ios ios7 uikit uistatusbar
20个回答
113
投票

可能的根本原因

我遇到了同样的问题,并且发现它正在发生,因为我没有在我的应用程序窗口中设置根视图控制器。

我实施UIViewControllerpreferredStatusBarStyle用于UITabBarController,它控制了屏幕上的视图外观。

当我将根视图控制器设置为指向此UITabBarController时,状态栏更改开始正常工作,正如预期的那样(并且调用了preferredStatusBarStyle方法)。

(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ... // other view controller loading/setup code

    self.window.rootViewController = rootTabBarController;
    [self.window makeKeyAndVisible];
    return YES;
}

替代方法(在iOS 9中已弃用)

或者,您可以根据需要在每个视图控制器中调用以下方法之一,而不必使用setNeedsStatusBarAppearanceUpdate

[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

要么

[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];

请注意,如果使用此方法,还需要在plist文件中将UIViewControllerBasedStatusBarAppearance设置为NO


6
投票

除了serenn的答案,如果你使用modalPresentationStyle(例如.overCurrentContext)呈现视图控制器,你还应该在新呈现的视图控制器上调用它:

presentedViewController.modalPresentationCapturesStatusBarAppearance = true

不要忘记在显示的视图控制器中覆盖preferredStatusBarStyle


5
投票

如果你的viewController在UINavigationController下。

子类UINavigationController并添加

override var preferredStatusBarStyle: UIStatusBarStyle {
    return topViewController?.preferredStatusBarStyle ?? .default
}

将调用ViewController的preferredStatusBarStyle


4
投票

iOS 7中的UIStatusBarStyle

iOS 7中的状态栏是透明的,后面的视图显示出来。

状态栏的样式是指其内容的外观。在iOS 7中,状态栏内容为暗(UIStatusBarStyleDefault)或浅(UIStatusBarStyleLightContent)。 UIStatusBarStyleBlackTranslucentUIStatusBarStyleBlackOpaque都在iOS 7.0中被弃用。请改用UIStatusBarStyleLightContent

如何改变UIStatusBarStyle

如果状态栏下方是导航栏,则会调整状态栏样式以匹配导航栏样式(UINavigationBar.barStyle):

具体来说,如果导航栏样式为UIBarStyleDefault,则状态栏样式为UIStatusBarStyleDefault;如果导航栏样式是UIBarStyleBlack,状态栏样式将是UIStatusBarStyleLightContent

如果状态栏下方没有导航栏,则应用程序运行时,各个视图控制器可以控制和更改状态栏样式。

-[UIViewController preferredStatusBarStyle]是iOS 7中添加的新方法。可以重写它以返回首选状态栏样式:

- (UIStatusBarStyle)preferredStatusBarStyle
  {
      return UIStatusBarStyleLightContent;
  }

如果状态栏样式应由子视图控制器而不是self控制,则覆盖-[UIViewController childViewControllerForStatusBarStyle]以返回该子视图控制器。

如果您希望选择不使用此行为并使用-[UIApplication statusBarStyle]方法设置状态栏样式,请将UIViewControllerBasedStatusBarAppearance键添加到应用程序的Info.plist文件中,并为其指定值NO。


4
投票

如果有人使用导航控制器并希望他们的所有导航控制器都具有黑色风格,你可以在Swift 3中编写UINavigationController的扩展,它将适用于所有导航控制器(而不是将其分配给一个控制器)时间)。

extension UINavigationController {

    override open func viewDidLoad() {
        super.viewDidLoad()

        self.navigationBar.barStyle = UIBarStyle.black
    }

}

4
投票

Swift 4.2 and above

正如selected answer中提到的,根本原因是检查你的窗口根视图控制器对象。

您的流程结构的可能情况

  • 自定义UIViewController对象是窗口根视图控制器 您的窗口根视图控制器是一个UIViewController对象,它还根据您的应用程序流程添加或删除导航控制器或tabController。 如果您的应用程序在没有选项卡的导航堆栈上具有预登录流程并且使用选项卡发布登录流程并且可能每个选项卡还包含导航控制器,则通常使用这种流程。
  • TabBarController对象是窗口根视图控制器 这是窗口根视图控制器是tabBarController的流程,可能每个选项卡都包含导航控制器。
  • NavigationController对象是窗口根视图控制器 这是窗口根视图控制器是navigationController的流程。 我不确定是否有可能在现有导航控制器中添加标签栏控制器或新导航控制器。但是如果存在这种情况,我们需要将状态栏样式控件传递给下一个容器。所以,我在UINavigationController扩展中添加了相同的检查来查找childForStatusBarStyle

使用以下扩展,它处理所有上述情况 -

extension UITabBarController {
    open override var childForStatusBarStyle: UIViewController? {
        return selectedViewController?.childForStatusBarStyle ?? selectedViewController
    }
}

extension UINavigationController {
    open override var childForStatusBarStyle: UIViewController? {
        return topViewController?.childForStatusBarStyle ?? topViewController
    }
}

extension AppRootViewController {
    open override var preferredStatusBarStyle: UIStatusBarStyle {
        return children.first { $0.childForStatusBarStyle != nil }?.childForStatusBarStyle?.preferredStatusBarStyle ?? .default
    }
}
  • 你不需要在UIViewControllerBasedStatusBarAppearance中使用info.plist键,因为它默认为true

要考虑更复杂的流程的要点

  • 如果您以模态方式呈现新流,它将从现有状态栏样式流中分离。因此,假设您正在呈现NewFlowUIViewController然后将新导航或tabBar控制器添加到NewFlowUIViewController,然后添加NewFlowUIViewController的扩展以管理更多视图控制器的状态栏样式。
  • 如果在呈现模态时设置除fullScreen之外的modalPresentationStyle,则必须将modalPresentationCapturesStatusBarAppearance设置为true,以便呈现的视图控制器必须接收状态栏外观控件。

1
投票

在Swift中用于任何类型的UIViewController:

在你的AppDelegate集:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    window!.rootViewController = myRootController
    return true
}

myRootController可以是任何种类的UIViewController,例如UITabBarControllerUINavigationController

然后,像这样覆盖这个根控制器:

class RootController: UIViewController {
    override func preferredStatusBarStyle() -> UIStatusBarStyle {
        return .LightContent
    }
}

这将改变整个应用程序中状态栏的外观,因为根控制器全权负责状态栏的外观。

请记住在View controller-based status bar appearance中将属性Info.plist设置为YES以使其工作(这是默认设置)。


1
投票

Swift 3 iOS 10解决方案:

override var preferredStatusBarStyle: UIStatusBarStyle {
    return .lightContent
 }

0
投票

这是我解决这个问题的方法。

定义一个名为AGViewControllerAppearance的协议。

AGViewControllerAppearance.h

#import <Foundation/Foundation.h>

@protocol AGViewControllerAppearance <NSObject>

@optional

- (BOOL)showsStatusBar;
- (BOOL)animatesStatusBarVisibility;
- (UIStatusBarStyle)preferredStatusBarStyle;
- (UIStatusBarAnimation)prefferedStatusBarAnimation;

@end

在UIViewController上定义一个名为Upgrade的类别。

的UIViewController + Upgrade.h

#import <UIKit/UIKit.h>

@interface UIViewController (Upgrade)

//
//  Replacements
//

- (void)upgradedViewWillAppear:(BOOL)animated;

@end

的UIViewController + Upgrade.m

#import "UIViewController+Upgrade.h"

#import <objc/runtime.h>

#import "AGViewControllerAppearance.h" // This is the appearance protocol

@implementation UIViewController (Upgrade)

+ (void)load
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wselector"
    Method viewWillAppear = class_getInstanceMethod(self, @selector(viewWillAppear:));
#pragma clang diagnostic pop
    Method upgradedViewWillAppear = class_getInstanceMethod(self, @selector(upgradedViewWillAppear:));
    method_exchangeImplementations(viewWillAppear, upgradedViewWillAppear);
}

#pragma mark - Implementation

- (void)upgradedViewWillAppear:(BOOL)animated
{
    //
    //  Call the original message (it may be a little confusing that we're
    //  calling the 'same' method, but we're actually calling the original one :) )
    //

    [self upgradedViewWillAppear:animated];

    //
    //  Implementation
    //

    if ([self conformsToProtocol:@protocol(AGViewControllerAppearance)])
    {
        UIViewController <AGViewControllerAppearance> *viewControllerConformingToAppearance =
        (UIViewController <AGViewControllerAppearance> *)self;

        //
        //  Status bar
        //

        if ([viewControllerConformingToAppearance respondsToSelector:@selector(preferredStatusBarStyle)])
        {
            BOOL shouldAnimate = YES;

            if ([viewControllerConformingToAppearance respondsToSelector:@selector(animatesStatusBarVisibility)])
            {
                shouldAnimate = [viewControllerConformingToAppearance animatesStatusBarVisibility];
            }

            [[UIApplication sharedApplication] setStatusBarStyle:[viewControllerConformingToAppearance preferredStatusBarStyle]
                                                        animated:shouldAnimate];
        }

        if ([viewControllerConformingToAppearance respondsToSelector:@selector(showsStatusBar)])
        {
            UIStatusBarAnimation animation = UIStatusBarAnimationSlide;

            if ([viewControllerConformingToAppearance respondsToSelector:@selector(prefferedStatusBarAnimation)])
            {
                animation = [viewControllerConformingToAppearance prefferedStatusBarAnimation];
            }

            [[UIApplication sharedApplication] setStatusBarHidden:(! [viewControllerConformingToAppearance showsStatusBar])
                                                    withAnimation:animation];
        }
    }
}

@end

现在,是时候说你的视图控制器正在实现AGViewControllerAppearance协议。

例:

@interface XYSampleViewController () <AGViewControllerAppearance>

... the rest of the interface

@end

当然,您可以从协议中实现其余的方法(showsStatusBar,animatesStatusBarVisibility,prefferedStatusBarAnimation),UIViewController + Upgrade将根据它们提供的值进行正确的自定义。


0
投票

如果有人用UISearchController遇到这个问题。只需创建一个新的UISearchController子类,然后将下面的代码添加到该类中:

override func preferredStatusBarStyle() -> UIStatusBarStyle {
    return .LightContent
}

0
投票

请注意,使用self.navigationController.navigationBar.barStyle = UIBarStyleBlack;解决方案时

一定要去你的plist并将“查看基于控制器的状态栏外观”设置为YES。如果它不,它将无法正常工作。


986
投票

对于使用UINavigationController的任何人:

UINavigationController没有转发preferredStatusBarStyle呼叫其子视图控制器。相反,它管理自己的状态 - 正如它应该的那样,它绘制在状态栏所在的屏幕顶部,因此应该负责它。因此,在导航控制器内的VC中实现preferredStatusBarStyle将不会做任何事情 - 它们永远不会被调用。

诀窍是UINavigationController用来决定返回UIStatusBarStyleDefaultUIStatusBarStyleLightContent的内容。它的基础是它的UINavigationBar.barStyle。默认(UIBarStyleDefault)导致黑暗前景UIStatusBarStyleDefault状态栏。而UIBarStyleBlack将给出一个UIStatusBarStyleLightContent状态栏。

TL; DR:

如果你想在UIStatusBarStyleLightContent上使用UINavigationController

self.navigationController.navigationBar.barStyle = UIBarStyleBlack;

0
投票

大多数答案不包括childViewControllerForStatusBarStyleUINavigationController方法的良好实施。根据我的经验,您应该处理在导航控制器上显示透明视图控制器时的情况。在这些情况下,您应该将控制传递给模态控制器(visibleViewController),但不会在它消失时传递。

override var childViewControllerForStatusBarStyle: UIViewController? {
  var childViewController = visibleViewController
  if let controller = childViewController, controller.isBeingDismissed {
    childViewController = topViewController
  }
  return childViewController?.childViewControllerForStatusBarStyle ?? childViewController
}

-2
投票

NavigationController或TabBarController是需要提供样式的。以下是我的解决方法:https://stackoverflow.com/a/39072526/242769


97
投票

所以我实际上为UINavigationController添加了一个类别,但使用了这些方法:

-(UIViewController *)childViewControllerForStatusBarStyle;
-(UIViewController *)childViewControllerForStatusBarHidden;

并让那些返回当前可见的UIViewController。这使得当前可见视图控制器设置其自己的首选样式/可见性。

这是一个完整的代码片段:

在Swift中:

extension UINavigationController {

    public override func childViewControllerForStatusBarHidden() -> UIViewController? {
        return self.topViewController
    }

    public override func childViewControllerForStatusBarStyle() -> UIViewController? {
        return self.topViewController
    }
}

在Objective-C中:

@interface UINavigationController (StatusBarStyle)

@end

@implementation UINavigationController (StatusBarStyle)

-(UIViewController *)childViewControllerForStatusBarStyle {
    return self.topViewController;
}

-(UIViewController *)childViewControllerForStatusBarHidden {
    return self.topViewController;
}

@end

为了更好的衡量,以下是它在UIViewController中的实现方式:

在斯威夫特

override public func preferredStatusBarStyle() -> UIStatusBarStyle {
    return .LightContent
}

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

在Objective-C中

-(UIStatusBarStyle)preferredStatusBarStyle {
    return UIStatusBarStyleLightContent; // your own style
}

- (BOOL)prefersStatusBarHidden {
    return NO; // your own visibility code
}

最后,确保您的应用程序plist没有将“基于控制器的状态栏外观视图”设置为NO。删除该行或将其设置为YES(我相信现在是iOS 7的默认值?)


62
投票

对于仍在努力解决这个问题的人来说,swift中的这个简单扩展应该可以解决您的问题。

extension UINavigationController {
    override open var childViewControllerForStatusBarStyle: UIViewController? {
        return self.topViewController
    }
}

15
投票

Tyson的答案对于在UINavigationController中将状态栏颜色更改为白色是正确的。

如果有人希望通过在AppDelegate中编写代码来完成相同的结果,那么使用下面的代码并将其写入AppDelegate's didFinishLaunchingWithOptions方法中。

并且不要忘记在.plist文件中将UIViewControllerBasedStatusBarAppearance设置为YES,否则更改将不会反映出来。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
     // status bar appearance code
     [[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];

     return YES;
}

13
投票

我的应用程序使用了所有三个:UINavigationControllerUISplitViewControllerUITabBarController,因此这些似乎都控制了状态栏,并将导致preferedStatusBarStyle不被称为他们的孩子。要覆盖此行为,您可以像其他提到的答案一样创建扩展。这是Swift 4中所有三个的扩展。希望Apple更清楚这种东西。

extension UINavigationController {
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return self.topViewController
    }

    open override var childViewControllerForStatusBarHidden: UIViewController? {
        return self.topViewController
    }
}

extension UITabBarController {
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return self.childViewControllers.first
    }

    open override var childViewControllerForStatusBarHidden: UIViewController? {
        return self.childViewControllers.first
    }
}

extension UISplitViewController {
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return self.childViewControllers.first
    }

    open override var childViewControllerForStatusBarHidden: UIViewController? {
        return self.childViewControllers.first
    }
}

编辑:更新Swift 4.2 API更改

extension UINavigationController {
    open override var childForStatusBarStyle: UIViewController? {
        return self.topViewController
    }

    open override var childForStatusBarHidden: UIViewController? {
        return self.topViewController
    }
}

extension UITabBarController {
    open override var childForStatusBarStyle: UIViewController? {
        return self.children.first
    }

    open override var childForStatusBarHidden: UIViewController? {
        return self.children.first
    }
}

extension UISplitViewController {
    open override var childForStatusBarStyle: UIViewController? {
        return self.children.first
    }

    open override var childForStatusBarHidden: UIViewController? {
        return self.children.first
    }
}

9
投票

Hippo答案的补充:如果您使用的是UINavigationController,那么最好添加一个类别:

//  UINavigationController+StatusBarStyle.h:

@interface UINavigationController (StatusBarStyle)

@end



//  UINavigationController+StatusBarStyle.m:

@implementation UINavigationController (StatusBarStyle)

- (UIStatusBarStyle)preferredStatusBarStyle
{
    //also you may add any fancy condition-based code here
    return UIStatusBarStyleLightContent;
}

@end

该解决方案可能比转换为即将弃用的行为更好。


8
投票

对于UINavigationControllers来说,@ serenn的answer仍然很棒。但是,对于swift 3,childViewController函数已更改为vars。所以UINavigationController扩展代码应该是:

override open var childViewControllerForStatusBarStyle: UIViewController? {
  return topViewController
}

override open var childViewControllerForStatusBarHidden: UIViewController? {
  return topViewController
}

然后在视图控制器中应该指定状态栏样式:

override var preferredStatusBarStyle: UIStatusBarStyle {
   return .lightContent
}

8
投票

在UINavigationController上,没有调用preferredStatusBarStyle,因为它的topViewControllerself更受欢迎。因此,要在UINavigationController上调用preferredStatusBarStyle,您需要更改其childViewControllerForStatusBarStyle

要为一个UINavigationController(我的推荐)做到这一点:

class MyRootNavigationController: UINavigationController {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
    override var childViewControllerForStatusBarStyle: UIViewController? {
        return nil
    }
}

要为所有UINavigationController执行此操作(警告:它会影响UIDocumentPickerViewController,UIImagePickerController等):

extension UINavigationController {
    open override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return nil
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.