不调用preferredStatusBarStyle

trg*_*ofi 250 uikit ios uistatusbar ios7

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

Tys*_*son 998

对于使用UINavigationController的任何人:

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

诀窍在于UINavigationController决定返回什么UIStatusBarStyleDefault或者用途的用途UIStatusBarStyleLightContent.它以此为基础UINavigationBar.barStyle.默认(UIBarStyleDefault)会生成暗前景UIStatusBarStyleDefault状态栏.并UIBarStyleBlack会给出一个UIStatusBarStyleLightContent状态栏.

TL; DR:

如果你想UIStatusBarStyleLightContentUINavigationController使用:

self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
Run Code Online (Sandbox Code Playgroud)

  • 太好了!请注意,如果您隐藏导航栏(将`navigationBarHidden`设置为'YES`),则实际上会在子视图控制器上调用`preferredStatusBarStyle`. (57认同)
  • 谢谢你的回答.如果要为所有导航栏设置barStyle,请调用`[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack]` (23认同)
  • 完美答案.SO上的其他答案都没有考虑到UINavigationController.把头撞到键盘上2个小时. (14认同)
  • 在 iOS 15 中,navigationBar 似乎不再根据“barStyle”决定其状态栏样式,而是似乎基于 userInterfaceStyle(深色或浅色模式)。可以通过设置“overrideUserInterfaceStyle”在 UINavigationBar 上覆盖此设置,但这将导致后退列表菜单(长按后退按钮)也具有暗模式样式外观。 (13认同)
  • 感谢@Patrick指出`navigationBarHidden`设置为`YES`实际上会调用`preferredStatusBarStyle`,并向那些可能偶然发现的人发出警告:它适用于`navigationBarHidden`,但不适用于`navigationBar.hidden`! (10认同)
  • 应该是显而易见的,但是您还需要在Info.plist中将"基于控制器的状态栏外观视图"设置为YES,以使其正常工作. (4认同)
  • 很棒的提示,你也可以从Storyboard中做到这一点. (3认同)
  • 对于 Swift 3 你做`self.navigationController?.navigationBar.barStyle = .black` (2认同)
  • 谢谢.神圣的moly,iOS有时候是坑.如果它不是Stack Overflow的令人愉快的贡献者,我很久以前就抛弃了这个平台. (2认同)

Abd*_*ahC 115

可能的根本原因

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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

或者,您可以根据需要在每个视图控制器中调用以下方法之一,具体取决于其背景颜色,而不必使用setNeedsStatusBarAppearanceUpdate:

[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
Run Code Online (Sandbox Code Playgroud)

要么

[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];
Run Code Online (Sandbox Code Playgroud)

请注意,您还需要设置UIViewControllerBasedStatusBarAppearanceNO如果您使用此方法在plist文件.

  • 我遇到了和你一样的问题,没有设置根视图控制器.你到底怎么找到的? (2认同)
  • 我在应用程序中发现的一个相关问题是具有全屏子视图控制器的视图控制器,该控制器未覆盖childViewControllerForStatusBarStyle和childViewControllerForStatusBarHidden以返回该子视图控制器.如果您有自己的视图控制器层次结构,则需要提供这些方法以通知系统应使用哪个视图控制器来确定状态栏样式. (2认同)

ser*_*enn 98

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

-(UIViewController *)childViewControllerForStatusBarStyle;
-(UIViewController *)childViewControllerForStatusBarHidden;
Run Code Online (Sandbox Code Playgroud)

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

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

在Swift中:

extension UINavigationController {

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

    public override func childViewControllerForStatusBarStyle() -> UIViewController? {
        return self.topViewController
    }
}
Run Code Online (Sandbox Code Playgroud)

在Objective-C中:

@interface UINavigationController (StatusBarStyle)

@end

@implementation UINavigationController (StatusBarStyle)

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

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

@end
Run Code Online (Sandbox Code Playgroud)

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

在斯威夫特

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

override func prefersStatusBarHidden() -> Bool {
    return false
}
Run Code Online (Sandbox Code Playgroud)

在Objective-C中

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

- (BOOL)prefersStatusBarHidden {
    return NO; // your own visibility code
}
Run Code Online (Sandbox Code Playgroud)

最后,确保你的应用程序plist中具有"查看基于控制器的状态栏外观"设置为NO.删除该行或将其设置为YES(我认为现在是iOS 7的默认值?)

  • 这是错误的,并且在 iOS 13.4 中被破坏。因为在 Swift 中扩展 Objective C 类是通过 Objective C 类来实现的。不建议通过 Objective C 类别重写方法,这可能会破坏。请参阅/sf/answers/2679226231/ (2认同)

Ale*_*own 70

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

extension UINavigationController {
    override open var childForStatusBarStyle: UIViewController? {
        return self.topViewController
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 你先生应该获得一枚奖章. (6认同)
  • iOS 14、15 无法运行 (6认同)
  • 非常感谢你.我正在返回visibleViewController而没有成功. (2认同)
  • 这是错误的,并且在 iOS 13.4 中被破坏。因为在 Swift 中扩展 Objective C 类是通过 Objective C 类来实现的。不建议通过 Objective C 类别重写方法,这可能会破坏。请参阅/sf/answers/2679226231/ (2认同)

Lui*_*uis 18

我的应用程序使用的所有三种:UINavigationController,UISplitViewController,UITabBarController,因此这些似乎都采取控制在状态栏,将导致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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是错误的,并且在 iOS 13.4 中被破坏。因为在 Swift 中扩展 Objective C 类是通过 Objective C 类来实现的。不建议通过 Objective C 类别重写方法,这可能会破坏。请参阅/sf/answers/2679226231/ (2认同)

Yog*_*har 15

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

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

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

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

     return YES;
}
Run Code Online (Sandbox Code Playgroud)


Art*_*mov 10

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
Run Code Online (Sandbox Code Playgroud)

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

  • 您应该使用子类,而不是类别。 (2认同)
  • @ArtemAbramov因为UINavigationController已经实现了`preferredStatusBarStyle`并且具有UINavigationController特定的逻辑.现在这个逻辑基于`navigationBar.barStyle`但我可以看到添加了额外的检查(例如``UISearchDisplayController`移动到隐藏导航栏模式).通过覆盖默认逻辑,您可以放弃所有这些功能,让自己在将来烦恼的'wtf'时刻开启.请参阅上面的答案,了解正确的方法,同时仍支持内置的导航控制器行为. (2认同)

Cœu*_*œur 9

在UINavigationController上,preferredStatusBarStyle未调用,因为它topViewController是首选self.因此,要preferredStatusBarStyle在UINavigationController上调用,您需要更改它childViewControllerForStatusBarStyle.

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

class MyRootNavigationController: UINavigationController {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
    override var childViewControllerForStatusBarStyle: UIViewController? {
        return nil
    }
}
Run Code Online (Sandbox Code Playgroud)

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

extension UINavigationController {
    open override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return nil
    }
}
Run Code Online (Sandbox Code Playgroud)


fri*_*rin 9

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

presentedViewController.modalPresentationCapturesStatusBarAppearance = true
Run Code Online (Sandbox Code Playgroud)

别忘了还要覆盖preferredStatusBarStyle显示的视图控制器中的。


And*_*rna 9

iOS 13 解决方案

UINavigationControllerUIViewController(谁知道)的子类!

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

以下任何一种方法都应该有效:

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

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

      class MyNavigationController: UINavigationController {
          override var preferredStatusBarStyle: UIStatusBarStyle {
              .lightContent
          }
      }
      
      Run Code Online (Sandbox Code Playgroud)

      或者

      extension UINavigationController {
          open override var preferredStatusBarStyle: UIStatusBarStyle {
              .lightContent
          }
      }
      
      Run Code Online (Sandbox Code Playgroud)
  3. 覆盖childForStatusBarStyle范围内UINavigationController

    • childForStatusBarStyle( doc ) - 当系统需要视图控制器用于确定状态栏样式时调用
    • 根据苹果的文档,

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

    • 换句话说,如果这里不执行方案三,系统就会退回到上面的方案二。
    • 子类或扩展 UINavigationController

      class MyNavigationController: UINavigationController {
          override var childForStatusBarStyle: UIViewController? {
              topViewController
          }
      }
      
      Run Code Online (Sandbox Code Playgroud)

      或者

      extension UINavigationController {    
          open override var childForStatusBarStyle: UIViewController? {
              topViewController
          }
      }
      
      Run Code Online (Sandbox Code Playgroud)
    • 你可以返回任何你喜欢的视图控制器。我推荐以下之一:

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

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

PS 我的代码使用 Swift 5.1 语法

  • 您通过扩展进行的覆盖并不是真正的覆盖。这是对语言的不安全滥用。这很容易坏掉。 (3认同)

Joh*_*ker 8

对于UINavigationControllers来说,@ serenn的答案仍然是一个很好的答案.但是,对于swift 3,childViewController函数已更改为vars.所以UINavigationController扩展代码应该是:

override open var childViewControllerForStatusBarStyle: UIViewController? {
  return topViewController
}

override open var childViewControllerForStatusBarHidden: UIViewController? {
  return topViewController
}
Run Code Online (Sandbox Code Playgroud)

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

override var preferredStatusBarStyle: UIStatusBarStyle {
   return .lightContent
}
Run Code Online (Sandbox Code Playgroud)

  • 这是错误的,并且在 iOS 13.4 中被破坏。因为在 Swift 中扩展 Objective C 类是通过 Objective C 类来实现的。不建议通过 Objective C 类别重写方法,这可能会破坏。请参阅/sf/answers/2679226231/ (2认同)

abh*_*dal 8

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
    }
}
Run Code Online (Sandbox Code Playgroud)
  • 默认情况下,您不需要UIViewControllerBasedStatusBarAppearance键入info.plist

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

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

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

  • 这是错误的,并且在 iOS 13.4 中被破坏。因为在 Swift 中扩展 Objective C 类是通过 Objective C 类来实现的。不建议通过 Objective C 类别重写方法,这可能会破坏。请参阅/sf/answers/2679226231/ (3认同)

Pow*_*wHu 6

如果你的viewController在UINavigationController下.

子类UINavigationController并添加

override var preferredStatusBarStyle: UIStatusBarStyle {
    return topViewController?.preferredStatusBarStyle ?? .default
}
Run Code Online (Sandbox Code Playgroud)

preferredStatusBarStyle将调用ViewController .