UIStatusBarStyle PreferredStatusBarStyle在iOS 7上不起作用


110

在使用Xcode 5 for iOS 7构建的iPhone应用程序UIViewControllerBasedStatusBarAppearance=YESinfo.plist,我设置了,并且ViewController我有以下代码:

-(UIStatusBarStyle) preferredStatusBarStyle
{
    return UIStatusBarStyleLightContent;
}

但是状态栏在黑色背景下仍然是黑色的。

我知道它可以通过设置这个程序范围的更改UIViewControllerBasedStatusBarAppearance=NOinfo.plist,但我确实需要改变这种对viewControllerviewController基础在运行时。


嗨,我遇到了与您提到的问题相同的问题。你得到解决方案了吗?请提供给我。
Noundla Sandeep

Answers:


281

我发现,如果您的ViewController在navigationController内,则navigationController会navigationBar.barStyle确定statusBarStyle。

将navigationBar设置barStyleUIBarStyleBlackTranslucent会显示白色状态栏文本(即UIStatusBarStyleLightContent),并显示UIBarStyleDefault黑色状态栏文本(即UIStatusBarStyleDefault)。

请注意,即使您通过完全更改navigationBar的颜色,这也适用barTintColor


这对我来说很有道理...太棒了
尼克

14
我相信这是因为UINavigationControllerpreferredStatusBarStyle不通过调用到ViewController它的主机,而是直接返回基于其navigationBarStyle。
mxcl 2013年

在这种情况下,视图不在导航控制器内部。
安德鲁·史密斯

认为条形样式优先于视图控制器中已实现的方法,并且仅在呈现模式视图时才具有直觉!
Matej 2014年

3
UIBarStyleBlackTranslucent 已弃用,请UIBarStyleBlack改用
Nhon Nguyen

87

好的,这就是窍门。您必须添加键“基于View Controller的状态栏”并将其值设置为No。

这与该键的含义相反,但是即使将值设置为No,您仍然可以更改状态栏的外观以及它是否在任何视图控制器中显示。因此它的行为类似于“是”,但将其设置为“否”!

现在,我可以将状态栏设置为白色或黑色。


6
对我来说,这是错误的。正如您期望的那样,密钥需要设置为“是”。我使用的是Xcode 5.1 iOS 7.1,因此可能已更改。
joel.d 2014年

我也使用Xcode 5.1和iOS 7.1,但对我没有任何帮助... STRANGE。
Arjun Mehta 2014年

我应该在哪里添加此密钥?
哈杜2014年

在您的[AppName] -Info.plist文件中-Saren
Inden

1
当Xcode6.0,iOS 8.0
bpolat

77

为了preferredStatusBarStyle()工作UINavigationControllerUITabBarController我添加了以下代码,它将从当前可见的视图控制器中获得首选的状态栏样式。

extension UITabBarController {
    public override func childViewControllerForStatusBarStyle() -> UIViewController? {
        return selectedViewController
    }
}

extension UINavigationController {
    public override func childViewControllerForStatusBarStyle() -> UIViewController? {
        return visibleViewController
    }
}

对于Swift 3,这些不是方法而是属性:

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

extension UINavigationController {
    open override var childViewControllerForStatusBarStyle: UIViewController? {
        return visibleViewController
    }
}

雨燕4.2的属性已被重命名:

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

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

用法

class ViewController: UIViewController {

    // This will be called every time the ViewController appears
    // Works great for pushing & popping
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }

}

6
到目前为止,这是最好的答案(对于使用UINavigationController或UITabBarController的应用程序
Kesava 2016年

1
这有什么用?
AnBisw

@Annjawn这些方法由UIKit使用。除了将其添加到项目中,您无需执行任何其他操作。
Daniel Wood

@DanielWood是的,我发现了这一点,并完全忘记了我在以前的一个项目中使用了完全相同的东西,尽管略有不同。
AnBisw

这确实是最好的答案
Musa almatri

33

我可能会晚一点,但是如果其他人正在寻找一个可行且经过验证的应用范围内的解决方案。

@mxcl是正确的描述原因。为了对其进行更正,我们只需创建一个扩展(或obj-c中的类别),即可覆盖UINavigationController的preferredSatusBarStyle()方法。这是Swift中的示例:

extension UINavigationController {
    public override func preferredStatusBarStyle() -> UIStatusBarStyle {
        if let rootViewController = self.viewControllers.first {
            return rootViewController.preferredStatusBarStyle()
        }
        return super.preferredStatusBarStyle()
    }
}

此代码仅提取第一个视图控制器(根视图控制器)并将其解包(在obj-c中只需检查其是否为nil)。如果解开成功(不是nil),则抓取rootViewControllers preferredStatusBarStyle。否则,我们只返回默认值。

希望这对可能需要它的人有所帮助。


2
在Swift 2.0中,您必须删除条件语句的“ as UIViewController”。
托马斯·卡尔蒙

精采,除了删除了“ as”语句外,我还做了一个修改,将其从“ first”更改为“ last”,这样,无论用户在堆栈顶部看到的是哪种视图控制器,都可以控制状态栏的颜色。很棒的工作,谢谢分享!
Unome

1
如果导航控制器没有任何视图控制器,则将导致无限循环。 return self.preferredStatusBarStyle()会调用此完全相同的方法。
bearMountain '02

1
就我而言,不是使用rootViewController,而是使用topViewController,因为在堆栈期间样式可能会更改。
里克·桑托斯

2
@Unome visibleViewController甚至会更好
心教堂

21

要在接受的答案中提供更多详细信息,请在您的应用程序委托的didFinishLaunchingWithOptions:方法中添加以下行:

[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;

然后,在您的Info.plist中,添加View controller-based status bar appearance并将其设置为NO

如果您希望整个应用程序使用相同的状态栏颜色,我相信应该这样做,而不是通过导航控制器。您可能拥有的屏幕不一定必须嵌入UINavigationController,或其他UINavigationController地方的其他子类中。

编辑:您也可以在不输入任何代码的情况下进行操作:https : //stackoverflow.com/a/18732865/855680


1
请注意,这种方式是从IOS 9.0弃用
穆罕默德·萨拉赫·

10

在viewDidLoad中只写这个

[self setNeedsStatusBarAppearanceUpdate];

只是这样做,它将起作用

你可以试试这个吗

Set UIViewControllerBasedStatusBarAppearance to NO.
Call [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

我在您的问题中看到的另一件事是您编写了这样的方法

 -(void)UIStatusBarStyle PreferredStatusBarStyle ()
        {
            return UIStatusBarStyle.LightContent;
        }

但应该是这样

-(UIStatusBarStyle)preferredStatusBarStyle{ 
    return UIStatusBarStyleLightContent; 
} 

这确实会导致调用preferredStatusBarStyle方法,但状态栏仍为黑色。
安德鲁·史密斯

请查看我更新的答案。.让我迅速知道它是否有效
用户1531343年

我最初的问题明确指出,我需要对状态栏进行逐个视图控制。
安德鲁·史密斯

您能参考我更新的问题检查您的代码吗?
用户1531343年

1
[self setNeedsStatusBarAppearanceUpdate];这么好的方法,谢谢!
Supertecnoboff

6

这是我解决的方法。通常,navigationController或tabBarController是决定状态栏外观(隐藏,颜色等)的那些。

因此,我最终将导航控制器子类化,并覆盖了preferredStatusBarStyle。如果当前可见的ViewContorller实现了StatusBarStyleHandler,则我要求将值用作样式,如果不是,则只返回默认值。

触发状态栏外观更新的方法是通过调用再次setNeedsStatusBarAppearanceUpdate触发preferredStatusBarStyle并根据方法返回的内容更新UI

public protocol StatusBarStyleHandler {
    var preferredStatusBarStyle: UIStatusBarStyle { get }
}

public class CustomNavigationCotnroller: UINavigationController {

    public override var preferredStatusBarStyle: UIStatusBarStyle {
        if let statusBarHandler = visibleViewController as? StatusBarStyleHandler {
            return statusBarHandler.preferredStatusBarStyle
        }

        return .default
    }
}

然后用法

public class SomeController: UIViewController, StatusBarStyleHandler {

    private var statusBarToggle = true

    // just a sample for toggling the status bar style each time method is called
    private func toggleStatusBarColor() {
        statusBarToggle = !statusBarToggle
        setNeedsStatusBarAppearanceUpdate()
    }

    public override var preferredStatusBarStyle: UIStatusBarStyle {
        return statusBarToggle ? .lightContent : .default
    }
}

如果您可以解释为什么以及如何解决此问题,那么这篇文章将得到很大的改善。

除了继承UINavigationController的子类,您还可以为UINavigationController创建一个扩展,并获得相同的结果,而无需子类化。
wilforeal

4

1)整个项目的一种设置:

如果有,请UIViewControllerBasedStatusBarAppearance从info.plist中删除键/值对,或者在NO不删除键/值对的情况下进行设置。如果您的info.plist中不可用,则什么也不做。NO此属性的默认值。

将以下代码添加到您的AppDelegate.m中:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
}

2)不同的View Controller的不同设置:

UIViewControllerBasedStatusBarAppearance键值对添加到info.plist并将其设置为YES

如果View Controller未嵌入到Navigation Controller中。假设是MyViewController。只需将以下代码添加到MyViewController.m文件即可。如果您的View Controller嵌入到Navigation Controller中,请创建一个新的Cocoa Touch Class并将其作为UINavigationController的子类。假设是MyNC。在右窗格的Storyboard上选择Navigation Controller View;在实用程序->身份检查器->自定义类->类中,键入“ MyNC”。将情节提要视图与“ MyNC”可可接触类链接后,将以下代码添加到MyNC.m:

- (BOOL)prefersStatusBarHidden {
    return NO;
}

-(UIStatusBarStyle)preferredStatusBarStyle {
    return UIStatusBarStyleLightContent;
}

默认情况下,iOS9中的UIViewControllerBasedStatusBarAppearance似乎包含值YES,因为我需要在.plist中手动添加该值并将其设置为NO才能正常工作。
Mohd Asim

4

即使这里有所有答案,我仍然没有找到适合我的确切解决方案,而是从丹尼尔的答案开始。我最终得到的是:

override var preferredStatusBarStyle: UIStatusBarStyle {
     return visibleViewController?.preferredStatusBarStyle ?? .lightContent
}

在导航控制器中(类似于tab,只是selectedViewController)。然后它将尊重:

override var preferredStatusBarStyle: UIStatusBarStyle {
     return .lightContent
}

在每个视图控制器中,除非另行设置。我不需要在setNeedsStatusBarAppearanceUpdate()任何地方调用,它只会在您到达每个视图控制器时进行更新。


2
经过数小时的努力,我最终得到了几乎相同的解决方案。
Scott Jungwirth

在某些时候,这似乎已经得到解决,仅在每个VC中使用preferredStatusBarStyle就可以了。
安德鲁·普拉默

4

iOS 13解决方案

投票最高的答案使用“旧版”代码👎

barStyle现在(iOS 13+)设置属性被视为“旧版自定义”。根据苹果的说法

在iOS 13及更高版本中,使用standardAppearance,compactAppearance和scrollEdgeAppearance属性自定义导航栏。您可以继续使用这些旧式访问器直接自定义导航栏的外观,但是您必须自己为不同的导航栏配置更新外观。

关于您的尝试-您的方向正确!

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

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

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

  1. preferredStatusBarStyle在内部覆盖UINavigationController

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

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

      要么

      extension UINavigationController {
          open override var preferredStatusBarStyle: UIStatusBarStyle {
              .lightContent
          }
      }
  2. 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
          }
      }
    • 您可以返回上面想要的任何视图控制器。我建议以下之一:

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

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

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


1
非常完整的答案,谢谢!另外,我挣扎了一段时间,在iOS 13上,该.default样式考虑了暗模式,因此没有记录,因此,如果您还支持以前的iOS版本,if #available(iOS 13, *) { return .darkContent } else { return .default }则可以尝试根据状态手动设置状态栏样式状态栏后面的颜色,该颜色为“亮”。
valcanaia

1
请注意,在Xcode 11.4 / iOS 13.4
Marc Etcheverry

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

尽管覆盖UINavigationController确实可以工作,但在Apple方面似乎是一个错误,默认情况下,它不返回childTopStatusBarStyle来返回其topViewController。例如,UITabBarController对其选项卡执行此操作。对我来说,没有理由UINavigationController是一个严格的容器控制器,用于承载“真实的” View控制器,而不是呈现自己的UI,应该“吃掉”这些状态栏样式。将为苹果制造雷达。
Igor Vasilev

1

如果万一您想在splashScreen期间隐藏statusBar但想将样式更改为浅色内容(Plist上的StatususBarInitiallyHidden必须为NO,则在splash上​​隐藏statusBar),则可以将其添加到appDelegate的didFinishLaunchingWithOptions方法中,以更改为lightContent。

[[UIApplication sharedApplication]setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
[[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleLightContent];

1

迅速的例子

在AppDelegate.swift中

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
    UIApplication.sharedApplication().statusBarStyle = UIStatusBarStyle.LightContent;

    return true
}

在info.plist中设置基于视图控制器的状态栏外观:否


1

如果您使用NavigationController,则可以子类化NavigationController以便其查询其子视图控制器

// MyCustomNavigationController

- (NSUInteger)supportedInterfaceOrientations {
    UIViewController *viewControllerToAsk = [self findChildVC];
    return [viewControllerToAsk supportedInterfaceOrientations];
}

- (BOOL)shouldAutorotate {
    UIViewController *viewControllerToAsk = [self findChildVC];
    return [viewControllerToAsk shouldAutorotate];
}

- (UIStatusBarStyle)preferredStatusBarStyle {
    UIViewController *viewControllerToAsk = [self findChildVC];
    return [viewControllerToAsk preferredStatusBarStyle];
}

- (UIViewController *)findChildVC {
    return self.viewControllers.firstObject;
}

1

斯威夫特4.2

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

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

请注意,在Xcode 11.4 / iOS 13.4
Marc Etcheverry

@MarcEtcheverry所以,为什么你对答案不满意?好像很奇怪
维亚切斯拉夫

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

@MarcEtcheverry'不推荐'!='从不使用它!'。对于2018年7月,答案是正确的。即使这个答案不是最新的,这也不是拒绝它的理由。我看不到未来
维亚切斯拉夫

0

您可以设置状态栏样式。它类似于IOS 6及以下的状态栏。
将此方法粘贴到您的视图控制器中

-(UIStatusBarStyle)preferredStatusBarStyle{
    return UIStatusBarStyleBlackOpaque;
}

并从视图中调用此方法确实像这样加载

if([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f)
    {
       [self setNeedsStatusBarAppearanceUpdate];
    }

你是说[self setStatusBarNeedsUpdate]第二个方块吗?(或者至少是其他东西)。
mxcl

0

我只想为我遇到的特定情况添加注释。我的应用程序中有另一个UIWindow,用于显示聊天面,该聊天面一直在整个应用程序中浮动。这样做导致上述解决方案均无效,而且我不确定为什么!我所注意到的是,新UIWindow中的ViewController是这样做的原因!而且,如果我想更改状态栏样式,则必须在新UIWindow的视图控制器中进行操作。

本说明可能会帮助结构相似的其他人!因此,基本上,您可以在新UIWindow的ViewController中应用上述解决方案。

同样,这是一个具体情况。

谢谢


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.