不会调用preferredStatusBarStyle


257

我遵循此线程进行覆盖-preferredStatusBarStyle,但未调用。我可以更改任何选项来启用它吗?(我在项目中使用XIB。)


它在什么情况下不会被调用:模拟器?在设备上?
2013年

@bneely他们两个。
trgoofi13年

您正在使用iOS 7模拟器,iOS 7设备,并且基本SDK是7.0?
2013年

@bneely iOS SDK 7.0显示在我的项目名称下方,这意味着我的基本SDK是7.0吗?
trgoofi13年

在构建设置中,“ Base SDK”是设置值的地方。听起来您的项目设置为7.0。
2013年

Answers:


117

可能的根本原因

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

UIViewController,其中我已经实现的preferredStatusBarStyle是在一个使用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文件中将设置UIViewControllerBasedStatusBarAppearanceNO


2
我和您有同样的问题,没有设置根视图控制器。你怎么发现的?
trgoofi13年

1
我怀疑框架中的某些内容没有收到通知setNeedsStatusBarAppearanceUpdate-当我进行此更改时,我的怀疑得到了证实。
AbdullahC

2
我在应用程序中发现的一个相关问题是具有全屏子视图控制器的视图控制器,该控制器未覆盖childViewControllerForStatusBarStyle和childViewControllerForStatusBarHidden来返回该子视图控制器。如果您拥有自己的视图控制器层次结构,则需要提供这些方法来通知系统应该使用哪个视图控制器来确定状态栏样式。
乔恩·斯坦梅兹

设置rootviewcontroller不会更改任何内容。您应该使用乔恩的评论。并在调用setneedsstatusbarappearanceUpdate时要小心。您应该从父母那里叫它上班。
doozMen 2014年

1
@河马你真是天才!! 您如何发现这是因为未设置rootviewcontroller?
ViruMax 2014年

1019

对于使用UINavigationController的任何人:

UINavigationController不转发就preferredStatusBarStyle调用它的子视图控制器。相反,它管理自己的状态-它应该在屏幕顶部状态栏所在的位置绘制图形,因此应对此负责。因此preferredStatusBarStyle,在nav控制器中的VC中实现什么都不做-永远不会调用它们。

诀窍是UINavigationController用来决定要返回UIStatusBarStyleDefault或的内容的方式UIStatusBarStyleLightContent。它基于它UINavigationBar.barStyle。默认(UIBarStyleDefault)会导致前景UIStatusBarStyleDefault状态栏变暗。并UIBarStyleBlack会给出一个UIStatusBarStyleLightContent状态栏。

TL; DR:

如果你想UIStatusBarStyleLightContentUINavigationController使用:

self.navigationController.navigationBar.barStyle = UIBarStyleBlack;

59
真好!请注意,preferredStatusBarStyle如果完全适当地隐藏了导航栏(设置navigationBarHiddenYES),则实际上会在子视图控制器上调用它。
Patrick Pijnappel 2013年

25
感谢您的回答。如果要为所有导航栏设置barStyle,请致电[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack]
-Thomas Desert

15
完美的答案。SO上的其他答案均未考虑UINavigationController。2小时将我的头撞在键盘上。
Ryan Alford 2014年

10
@Patrick表示实际上已经调用过navigationBarHiddenset ,因此对他们表示敬意,并警告那些可能迷失于此的人:它适用于,但不适用于!YESpreferredStatusBarStylenavigationBarHiddennavigationBar.hidden
jcaron 2014年

4
应该很明显,但是您还需要在Info.plist中将“基于视图控制器的状态栏外观”设置为YES才能起作用。
Code Baller

99

因此,我实际上向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的默认设置吗?)


看起来return self.topViewController;对我return self.visibleViewController;
有用

当您关闭它时,visibleViewController可以返回当前显示的模态控制器。真是可恶 使用topViewController。
本·辛克莱

1
@ d.lebedev好的,但是我认为这些问题都不适用。你不需要打电话super这个方法你真的想改变这个类型的所有控制器的行为
艾德

1
这不适用于iOS 9.3。我想这就是问题所在:这个问题特别重要,因为许多可可类都是使用类别实现的。您尝试覆盖的框架定义的方法本身可能已在一个类别中实现,因此未定义哪个实现优先。
vikingosegundo

2
这是错误的,并且在iOS 13.4中中断。因为在Swift中扩展目标C类是通过目标C类实现的。不建议通过Objective C类别覆盖方法,并且可能会破坏方法。参见stackoverflow.com/a/38274660/2438634
马克·埃切弗里

79

对于仍然为此感到困扰的任何人,迅速的此简单扩展都应该为您解决问题。

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

10
先生,您应该得到一枚奖牌。
尼坎斯'17

2
非常感谢你。我返回的是visibleViewController,但没有成功。
法比奥·萨拉塔(FábioSalata)

1
这是黄金。我在选项卡栏中嵌入了一个导航控制器,然后将其扔到了文件中,现在可以在任何位置更改状态栏的外观。
Vahid Amiri '18

2
这是错误的,并且在iOS 13.4中中断。因为在Swift中扩展目标C类是通过目标C类实现的。不建议通过Objective C类别覆盖方法,并且可能会破坏方法。请参阅stackoverflow.com/a/38274660/2438634
Marc Etcheverry

1
@MarcEtcheverry这个特定实例没有错。问题的事实是,其他对象/协议的子类(如UINavigationController)在动态分配中没有事先实现这些子类的冲突。实际的子类中没有默认值或实现,这就是为什么这是在不创建不必要的依赖关系(周期)的情况下跨应用程序实现此方法的最简洁方法。不幸的是,13.4似乎已经改变了这种行为。我猜想他们背后已经有很多年不存在的检查或实施了…………
TheCodingArt

20

我的应用程序使用的所有三种:UINavigationControllerUISplitViewControllerUITabBarController,因此这些似乎都采取控制在状态栏,将导致preferedStatusBarStyle不叫他们的孩子。要覆盖此行为,您可以像其他答案一样创建扩展名。这是Swift 4中所有这三个功能的扩展。希望苹果公司对这种东西更加清楚。

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

1
这是唯一可行的解​​决方案。SO上的所有答案都指向标准解决方案,该解决方案不适用于任何带有NavigationControllers的应用程序。谢谢!!!
侯曼

使用扩展来覆盖是错误的。那不安全。有多个更简单的解决方案。改用子类。
Sulthan

2
这是错误的,并且在iOS 13.4中中断。因为在Swift中扩展目标C类是通过目标C类实现的。不建议通过Objective C类别覆盖方法,并且可能会破坏方法。请参阅stackoverflow.com/a/38274660/2438634
Marc Etcheverry

1
@MarcEtcheverry这个特定实例没有错。问题的事实是,其他对象/协议的子类(如UINavigationController)在动态分配中没有事先实现这些子类的冲突。实际的子类中没有默认值或实现,这就是为什么这是在不创建不必要的依赖关系(周期)的情况下跨应用程序实现此方法的最简洁方法。不幸的是,13.4似乎已经改变了这种行为。我猜想他们背后已经有很多年不存在的检查或实施了…………
TheCodingArt

15

对于将中的状态栏颜色更改为白色,泰森的答案是正确的UINavigationController

如果有人想通过写入代码来达到相同的结果,AppDelegate则使用下面的代码并将其写入内部AppDelegate's didFinishLaunchingWithOptions方法。

并且不要忘记将设置UIViewControllerBasedStatusBarAppearanceYES中的.plist文件,否则更改将不会反映。

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

     return YES;
}

14

在UINavigationController上,preferredStatusBarStyle未调用,因为它topViewController优于self。因此,要preferredStatusBarStyle在UINavigationController上调用它,您需要更改其childViewControllerForStatusBarStyle

建议

在类中重写UINavigationController:

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

不建议替代

要对所有UINavigationController做到这一点,您可以重写扩展名(警告:它会影响UIDocumentPickerViewController,UIImagePickerController等),但是根据Swift文档您可能不应该这样做

extension UINavigationController {
    open override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return nil
    }
}

11

除了serenn的答案外,如果要向视图控制器提供一个modalPresentationStyle(例如.overCurrentContext),还应该在新显示的视图控制器上调用它:

presentedViewController.modalPresentationCapturesStatusBarAppearance = true

别忘了还要重写preferredStatusBarStyle所提供的视图控制器中的。


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

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


不要执行此操作,它暂时可以使用,但可能会破坏将来的行为。只需更改navBar样式-参见我的答案stackoverflow.com/a/19513714/505457
泰森(Tyson),

2
您应该使用子类,而不是类别。
shuiyouren 2013年

2Tyson:为什么会破坏未来的行为?preferredStatusBarStyle:是Apple设置状态栏样式的首选方法。
Artem Abramov

2shuiyouren:如果我只能使用类别并将其包含在我愿意去的每个地方,为什么我应该通过子类化来增加复杂性?无论如何,这是体系结构的问题,而不是实现的问题。
Artem Abramov

2
@ArtemAbramov因为UINavigationController已经实现preferredStatusBarStyle并执行UINavigationController特定的逻辑。现在,此逻辑基于,navigationBar.barStyle但我可以看到添加了其他检查(例如,UISearchDisplayController移动到隐藏导航栏模式)。通过覆盖默认逻辑,您可以松开所有这些功能,并在将来烦恼的“ wtf”时刻保持开放状态。请参阅上面的答案,以获取仍支持内置导航控制器行为的正确方法。
泰森

9

Swift 4.2及更高版本

所选答案中所述,根本原因是检查您的窗口根视图控制器对象。

流结构的可能情况

  • 自定义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

考虑更复杂流程的要点

  • 如果您以模态方式呈现新流程,则它将与现有状态栏样式流程分离。因此,假设您要展示一个NewFlowUIViewController,然后向添加新的导航或tabBar控制器NewFlowUIViewController,然后再添加的扩展名,NewFlowUIViewController以管理其他视图控制器的状态栏样式。

  • 如果您设置了modalPresentationStyle而不是fullScreen模态呈现时,则必须将其设置modalPresentationCapturesStatusBarAppearance为true,以便呈现的视图控制器必须接收状态栏外观控件。


很好的答案!
阿敏·贝纳里耶布

3
这是错误的,并且在iOS 13.4中中断。因为在Swift中扩展目标C类是通过目标C类实现的。不建议通过Objective C类别覆盖方法,并且可能会破坏方法。请参阅stackoverflow.com/a/38274660/2438634
Marc Etcheverry

@MarcEtcheverry这个特定实例没有错。问题的事实是,其他对象/协议的子类(如UINavigationController)在动态分配中没有事先实现这些子类的冲突。实际的子类中没有默认值或实现,这就是为什么这是在不创建不必要的依赖关系(周期)的情况下跨应用程序实现此方法的最简洁方法。不幸的是,13.4似乎已经改变了这种行为。我猜他们在后台进行检查或实施,这已经多年不存在了…………
TheCodingArt

8

iOS 13解决方案

UINavigationController 是的子类 UIViewController(谁知道🙃)!

因此,在呈现嵌入在导航控制器中的视图控制器时,您并不是真正在呈现嵌入式视图控制器。您正在展示导航控制器!UINavigationController作为的子类UIViewController,继承preferredStatusBarStylechildForStatusBarStyle,您可以根据需要设置。

以下任何一种方法都可以工作:

  1. 完全退出黑暗模式
    • 在你的 info.plist,添加以下属性:
      • 键- UIUserInterfaceStyle(又名“用户界面样式”)
      • 价值-轻
  2. preferredStatusBarStyle在内部覆盖UINavigationController

    • preferredStatusBarStyledoc)-视图控制器的首选状态栏样式
    • 子类或扩展 UINavigationController

      class MyNavigationController: UINavigationController {
          override var preferredStatusBarStyle: UIStatusBarStyle {
              .lightContent
          }
      }

      要么

      extension UINavigationController {
          open override var preferredStatusBarStyle: UIStatusBarStyle {
              .lightContent
          }
      }
  3. childForStatusBarStyle在内部覆盖UINavigationController

    • childForStatusBarStyledoc)-在系统需要视图控制器来确定状态栏样式时调用
    • 根据Apple的文件,

      “如果您的容器视图控制器从其子视图控制器之一派生其状态栏样式,请[覆盖此属性]并返回该子视图控制器。如果您返回nil或不重写此方法,则使用self的状态栏样式如果此方法的返回值更改,请调用setNeedsStatusBarAppearanceUpdate()方法。”

    • 换句话说,如果您在此处未实现解决方案3,则系统将退回到上述解决方案2。
    • 子类或扩展 UINavigationController

      class MyNavigationController: UINavigationController {
          override var childForStatusBarStyle: UIViewController? {
              topViewController
          }
      }

      要么

      extension UINavigationController {    
          open override var childForStatusBarStyle: UIViewController? {
              topViewController
          }
      }
    • 您可以返回上面想要的任何视图控制器。我建议以下之一:

      • topViewController(of UINavigationController)(doc)-导航堆栈顶部的视图控制器
      • visibleViewController(of UINavigationController)(doc)-与导航界面中当前可见视图相关联的视图控制器(提示:这可以包括“以模态形式显示在导航控制器本身顶部的视图控制器”)

注意:如果决定子类化UINavigationController,请记住通过IB中的身份检查器将该类应用于导航控制器。

PS我的代码使用Swift 5.1语法😎


屏幕旋转后,我的状态栏变黑。知道为什么吗?这仅在iPad Pro模拟器上发生。
Pedro Paulo Amorim

@PedroPauloAmorim,您可以提供更多信息吗?顶视图控制器如何显示(模态,全屏,显示)?它嵌套在导航控制器中吗?文字变黑还是背景变黑?你想达到什么目的?
安德鲁·基尔纳

我在整个应用程序中设置了指示灯状态栏。它以两次旋转的方式获取光,在第三轮中它变暗,并且从不返回光,甚至强迫重新绘制。这是在iPad Pro模拟器上发生的。视图将以全屏显示,并且不会嵌套在导航控制器中。只有文本变暗。
Pedro Paulo Amorim

首先如何设置灯光状态栏?
Andrew Kirna

3
通过扩展覆盖不是真正的覆盖。这是对语言的不安全滥用。那很容易破坏。
Sulthan

7

对于UINavigationControllers而言,@ serenn的答案仍然是一个很好的答案。但是,对于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
}

2
这是错误的,并且在iOS 13.4中中断。因为在Swift中扩展目标C类是通过目标C类实现的。不建议通过Objective C类别覆盖方法,并且可能会破坏方法。请参阅stackoverflow.com/a/38274660/2438634
Marc Etcheverry

@MarcEtcheverry这个特定实例没有错。问题的事实是,其他对象/协议的子类(如UINavigationController)在动态分配中没有事先实现这些子类的冲突。实际的子类中没有默认值或实现,这就是为什么这是在不创建不必要的依赖关系(周期)的情况下跨应用程序实现此方法的最简洁方法。不幸的是,13.4似乎已经改变了这种行为。我猜他们在后台进行检查或实施,这已经多年不存在了…………
TheCodingArt

6

如果您的viewController在UINavigationController下。

子类UINavigationController并添加

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

ViewController preferredStatusBarStyle将被调用。


1
看到这篇文章:medium.com/@
哈里·布鲁姆

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。


3

如果有人正在使用导航控制器,并且希望所有导航控制器都具有黑色样式,则可以在Swift 3中像这样向UINavigationController编写扩展,它将扩展应用于所有导航控制器(而不是将其分配给一个导航控制器)。时间)。

extension UINavigationController {

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

        self.navigationBar.barStyle = UIBarStyle.black
    }

}

1
但是,如果我的导航栏被隐藏了怎么办?
Slavcho '17

1
因为我需要隐藏导航并显示状态栏。
斯拉夫乔(Slavcho)

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为YES Info.plist即可(默认设置)。


@如何在swift3中?
飞机

1

Swift 3 iOS 10解决方案:

override var preferredStatusBarStyle: UIStatusBarStyle {
    return .lightContent
 }

1

大多数答案都没有包含childViewControllerForStatusBarStyle方法的良好实现UINavigationController。根据我的经验,您应该处理诸如在导航控制器上显示透明视图控制器的情况。在这些情况下,您应该将控制权传递给模态控制器(visibleViewController),而当它消失时则不能。

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

1

就我而言,我不小心将View / Navigation Controller显示为UIModalPresentationStyle.overFullScreen,这导致preferredStatusBarStyle未调用它。将其切换回之后UIModalPresentationStyle.fullScreen,一切正常。


1

至于iOS 13.4,将不会调用类别中的preferredStatusBarStyle方法UINavigationController,似乎不需要使用子类就可以选择使用swizzling。

例:

类别标题:

@interface UINavigationController (StatusBarStyle)
+ (void)setUseLightStatusBarStyle;
@end

实现方式:

#import "UINavigationController+StatusBarStyle.h"
#import <objc/runtime.h>

@implementation UINavigationController (StatusBarStyle)

void (^swizzle)(Class, SEL, SEL) = ^(Class c, SEL orig, SEL new){
    Method origMethod = class_getInstanceMethod(c, orig);
    Method newMethod = class_getInstanceMethod(c, new);
    if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
        class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
    else
        method_exchangeImplementations(origMethod, newMethod);
};

+ (void)setUseLightStatusBarStyle {
    swizzle(self.class, @selector(preferredStatusBarStyle), @selector(_light_preferredStatusBarStyle));
}

- (UIStatusBarStyle)_light_preferredStatusBarStyle {
    return UIStatusBarStyleLightContent;
}    
@end

AppDelegate.h中的用法:

#import "UINavigationController+StatusBarStyle.h"

[UINavigationController setUseLightStatusBarStyle];

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

当然,你可以实现的方法(其余showsStatusBaranimatesStatusBarVisibilityprefferedStatusBarAnimation从协议)的UIViewController +升级会基于其提供的值适当定制。


0

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

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

0

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

确保转到您的列表,并将“基于View Controller的状态栏外观”设置为YES。如果否,将无法正常工作。


在项目plist中将UIViewControllerBasedStatusBarAppearance设置为YES对我来说是不同的。我已经忘记了。
费罗

0

从Xcode 11.4开始, preferredStatusBarStyle UINavigationController扩展中属性不再起作用,因为将不会对其进行调用。

设置barStylenavigationBar.black作品确实,但如果添加子视图可能有光明和黑暗模式不同出场的导航栏,这将增加不必要的副作用。因为通过将设置barStyle为黑色,userInterfaceStyle然后嵌入在navigationBar中的视图的便始终具有与应用程序userInterfaceStyle.dark无关的视图userInterfaceStyle

我想出的正确解决方案是通过添加的子类UINavigationControllerpreferredStatusBarStyle在那里重写。如果然后对所有视图使用此自定义UINavigationController,则将处于保存状态。


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.