不调用preferredStatusBarStyle

时间:2013-09-26 07:42:14

标签: ios ios7 uikit uistatusbar

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

25 个答案:

答案 0 :(得分:971)

对于使用UINavigationController的任何人:

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

诀窍是UINavigationController用来决定UIStatusBarStyleDefaultUIStatusBarStyleLightContent的回报。它基于UINavigationBar.barStyle。默认值(UIBarStyleDefault)会生成暗前景UIStatusBarStyleDefault状态栏。 UIBarStyleBlack会提供UIStatusBarStyleLightContent状态栏。

<强> TL; DR:

如果您希望UIStatusBarStyleLightContent使用UINavigationController

self.navigationController.navigationBar.barStyle = UIBarStyleBlack;

答案 1 :(得分:113)

可能的根本原因

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

我在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文件中将UIViewControllerBasedStatusBarAppearance设置为NO

答案 2 :(得分:95)

所以我实际上在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中的实现方式:

在Swift中

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 NOT 将“基于控制器的状态栏外观视图”设置为NO。删除该行或将其设置为YES(我认为现在是iOS 7的默认值?)

答案 3 :(得分:60)

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

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

答案 4 :(得分: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;
}

答案 5 :(得分:11)

我的应用使用了全部三个: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
    }
}

答案 6 :(得分: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

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

答案 7 :(得分:8)

在UINavigationController上,未调用preferredStatusBarStyle,因为topViewController优先于self。因此,要在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
    }
}

答案 8 :(得分:7)

对于UINavigationControllers,上面的@ serenn&#39; s 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
}

答案 9 :(得分:5)

如果你的viewController在UINavigationController下。

UINavigationController子类并添加

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

将调用ViewController&#39; preferredStatusBarStyle

答案 10 :(得分:4)

iOS 7中的UIStatusBarStyle

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

状态栏的样式是指其内容的外观。在iOS 7中,状态栏内容为深色(UIStatusBarStyleDefault)或浅色(UIStatusBarStyleLightContent)。 iOS 7.0中不推荐使用UIStatusBarStyleBlackTranslucentUIStatusBarStyleBlackOpaque。请改用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。

答案 11 :(得分:4)

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

extension UINavigationController {

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

        self.navigationBar.barStyle = UIBarStyle.black
    }

}

答案 12 :(得分:3)

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

presentedViewController.modalPresentationCapturesStatusBarAppearance = true

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

答案 13 :(得分:3)

Swift 4.2及更高版本

selected answer中所述,根本原因是检查窗口的根视图控制器对象。

您的流结构的可能情况

  • 自定义UIViewController对象是窗口根视图控制器

    窗口根视图控制器是一个UIViewController对象,它根据您的应用程序流进一步添加或删除导航控制器或tabController。

    如果您的应用在导航堆栈中没有选项卡的登录前流程和带有选项卡的登录后流程,并且每个选项卡可能还包含导航控制器,则通常使用这种流程。

  • TabBarController对象是窗口根视图控制器

    这是窗口根视图控制器为tabBarController的流程,可能每个标签还包含导航控制器。

  • NavigationController对象是窗口根视图控制器

    这是窗口根视图控制器为navigationController的流程。

    我不确定是否有可能在现有的导航控制器中添加标签栏控制器或新的导航控制器。但是,如果发生这种情况,我们需要将状态栏样式控件传递给下一个容器。因此,我在UINavigationController扩展中添加了相同的检查,以找到l = [('A', 'B'),('C', 'B'),('J', 'K'),('E','D'),('X','B')] G=nx.Graph() G.add_edges_from(l) cc = list(nx.connected_components(G)) component = next(i for i in cc if ('A') in i) #{'B', 'X', 'C', 'A'} for x in l: if x[0] in component or x[1] in component: print (x) >>> ('A', 'B') >>> ('C', 'B') >>> ('X', 'B')

使用以下扩展名,可以处理以上所有情况-

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的扩展名,以管理其他视图控制器的状态栏样式。

  • 如果在模态显示时设置了{strong> modalPresentationStyle 而不是NewFlowUIViewController,则必须将fullScreen设置为true,以便所显示的视图控制器必须接收状态栏外观控件

答案 14 :(得分:1)

Swift 3 iOS 10解决方案:

raise ImproperlyConfigured("Error loading either pysqlite2 or sqlite3 modules (tried in that order): %s" % exc)
django.core.exceptions.ImproperlyConfigured: Error loading either pysqlite2 or sqlite3 modules (tried in that order): No module named '_sqlite3'

答案 15 :(得分:1)

就我而言,我不小心将视图/导航控制器显示为UIModalPresentationStyle.overFullScreen,这导致preferredStatusBarStyle未被调用。将其切换回UIModalPresentationStyle.fullScreen后,一切正常。

答案 16 :(得分:1)

iOS 13解决方案

UINavigationControllerUIViewController(谁知道?)的子类!

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

以下任何一种方法都可以使用:

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

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

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

      OR

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

      OR

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

      • {{1}中的topViewControllerdoc)-导航堆栈顶部的视图控制器
      • UINavigationController(共visibleViewController个)(doc)-与导航界面中当前可见视图关联的视图控制器(提示:这可以包括“已显示的视图控制器在导航控制器本身的顶部”)

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

P.S。我的代码使用Swift 5.1语法?

答案 17 :(得分:1)

对于iOS 13.4,preferredStatusBarStyle类别中的UINavigationController方法将不会被调用,izz绕似乎是唯一无需使用子类的选择。

示例:

类别标题:

@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];

答案 18 :(得分:0)

从Xcode 11.4开始,覆盖UINavigationController扩展中的preferredStatusBarStyle属性不再起作用,因为它将不会被调用。

barStyle的{​​{1}}设置为navigationBar确实可以,但是如果将子视图添加到navigationBar中,则在明暗模式下外观可能会有所不同,这会增加不必要的副作用。因为通过将.black设置为黑色,所以嵌入在navigationBar中的视图的barStyle将始终具有userInterfaceStyle,而与应用程序的userInterfaceStyle.dark无关。

我想出的正确解决方案是添加userInterfaceStyle的子类并在那里覆盖UINavigationController。如果您随后将此自定义UINavigationController用于所有视图,则将处于保存状态。

答案 19 :(得分:0)

大多数答案并不包括rabbitmqctl set_permissions -p app username '.*' '.*' '.*' 的{​​{1}}方法的良好实现。根据我的经验,您应该处理诸如在导航控制器上显示透明视图控制器的情况。在这种情况下,您应该将控制权传递给模态控制器(childViewControllerForStatusBarStyle),但不要在其消失时传递。

UINavigationController

答案 20 :(得分:0)

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

请务必转到您的plist并将“查看基于控制器的状态栏外观”设置为YES。如果它不,它将无法正常工作。

答案 21 :(得分:0)

在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以使其正常工作(这是默认设置)。

答案 22 :(得分:0)

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

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

答案 23 :(得分:0)

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

定义名为 AGViewControllerAppearance 的协议。

<强> AGViewControllerAppearance.h

#import <Foundation/Foundation.h>

@protocol AGViewControllerAppearance <NSObject>

@optional

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

@end

UIViewController 上定义一个名为升级的类别。

<强>的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

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

答案 24 :(得分:-2)

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