我遵循此线程进行覆盖-preferredStatusBarStyle
,但未调用。我可以更改任何选项来启用它吗?(我在项目中使用XIB。)
我遵循此线程进行覆盖-preferredStatusBarStyle
,但未调用。我可以更改任何选项来启用它吗?(我在项目中使用XIB。)
Answers:
我遇到了同样的问题,并发现这是正在发生的,因为我没有在应用程序窗口中设置根视图控制器。
的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;
}
另外,您可以根据背景色的不同,在每个视图控制器中调用以下方法之一,而不必使用setNeedsStatusBarAppearanceUpdate
:
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
要么
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];
请注意,如果您使用此方法,则还需要在plist文件中将设置UIViewControllerBasedStatusBarAppearance
为NO
。
setNeedsStatusBarAppearanceUpdate
-当我进行此更改时,我的怀疑得到了证实。
对于使用UINavigationController的任何人:
该UINavigationController
不转发就preferredStatusBarStyle
调用它的子视图控制器。相反,它管理自己的状态-它应该在屏幕顶部状态栏所在的位置绘制图形,因此应对此负责。因此preferredStatusBarStyle
,在nav控制器中的VC中实现什么都不做-永远不会调用它们。
诀窍是UINavigationController
用来决定要返回UIStatusBarStyleDefault
或的内容的方式UIStatusBarStyleLightContent
。它基于它UINavigationBar.barStyle
。默认(UIBarStyleDefault
)会导致前景UIStatusBarStyleDefault
状态栏变暗。并UIBarStyleBlack
会给出一个UIStatusBarStyleLightContent
状态栏。
TL; DR:
如果你想UIStatusBarStyleLightContent
在UINavigationController
使用:
self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
preferredStatusBarStyle
如果完全适当地隐藏了导航栏(设置navigationBarHidden
为YES
),则实际上会在子视图控制器上调用它。
[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack]
navigationBarHidden
set ,因此对他们表示敬意,并警告那些可能迷失于此的人:它适用于,但不适用于!YES
preferredStatusBarStyle
navigationBarHidden
navigationBar.hidden
因此,我实际上向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;
super
这个方法你真的想改变这个类型的所有控制器的行为
对于仍然为此感到困扰的任何人,迅速的此简单扩展都应该为您解决问题。
extension UINavigationController {
override open var childForStatusBarStyle: UIViewController? {
return self.topViewController
}
}
我的应用程序使用的所有三种:UINavigationController
,UISplitViewController
,UITabBarController
,因此这些似乎都采取控制在状态栏,将导致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
}
}
对于将中的状态栏颜色更改为白色,泰森的答案是正确的UINavigationController
。
如果有人想通过写入代码来达到相同的结果,AppDelegate
则使用下面的代码并将其写入内部AppDelegate's
didFinishLaunchingWithOptions
方法。
并且不要忘记将设置UIViewControllerBasedStatusBarAppearance
为YES
中的.plist文件,否则更改将不会反映。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// status bar appearance code
[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];
return YES;
}
在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
}
}
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
该解决方案可能比切换到即将弃用的行为更好。
preferredStatusBarStyle
并执行UINavigationController特定的逻辑。现在,此逻辑基于,navigationBar.barStyle
但我可以看到添加了其他检查(例如,UISearchDisplayController
移动到隐藏导航栏模式)。通过覆盖默认逻辑,您可以松开所有这些功能,并在将来烦恼的“ wtf”时刻保持开放状态。请参阅上面的答案,以获取仍支持内置导航控制器行为的正确方法。
如所选答案中所述,根本原因是检查您的窗口根视图控制器对象。
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,以便呈现的视图控制器必须接收状态栏外观控件。
UINavigationController
是的子类 UIViewController
(谁知道🙃)!
因此,在呈现嵌入在导航控制器中的视图控制器时,您并不是真正在呈现嵌入式视图控制器。您正在展示导航控制器!UINavigationController
作为的子类UIViewController
,继承preferredStatusBarStyle
和childForStatusBarStyle
,您可以根据需要设置。
以下任何一种方法都可以工作:
info.plist
,添加以下属性:
UIUserInterfaceStyle
(又名“用户界面样式”)preferredStatusBarStyle
在内部覆盖UINavigationController
preferredStatusBarStyle
(doc)-视图控制器的首选状态栏样式子类或扩展 UINavigationController
class MyNavigationController: UINavigationController {
override var preferredStatusBarStyle: UIStatusBarStyle {
.lightContent
}
}
要么
extension UINavigationController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
.lightContent
}
}
childForStatusBarStyle
在内部覆盖UINavigationController
childForStatusBarStyle
(doc)-在系统需要视图控制器来确定状态栏样式时调用“如果您的容器视图控制器从其子视图控制器之一派生其状态栏样式,请[覆盖此属性]并返回该子视图控制器。如果您返回nil或不重写此方法,则使用self的状态栏样式如果此方法的返回值更改,请调用setNeedsStatusBarAppearanceUpdate()方法。”
子类或扩展 UINavigationController
class MyNavigationController: UINavigationController {
override var childForStatusBarStyle: UIViewController? {
topViewController
}
}
要么
extension UINavigationController {
open override var childForStatusBarStyle: UIViewController? {
topViewController
}
}
您可以返回上面想要的任何视图控制器。我建议以下之一:
注意:如果决定子类化UINavigationController
,请记住通过IB中的身份检查器将该类应用于导航控制器。
PS我的代码使用Swift 5.1语法😎
对于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
}
如果您的viewController在UINavigationController下。
子类UINavigationController并添加
override var preferredStatusBarStyle: UIStatusBarStyle {
return topViewController?.preferredStatusBarStyle ?? .default
}
ViewController preferredStatusBarStyle
将被调用。
iOS 7中的UIStatusBarStyle
iOS 7中的状态栏是透明的,其后的视图显示完整。
状态栏的样式是指其内容的外观。在iOS 7中,状态栏的内容为深色(UIStatusBarStyleDefault
)或浅色(UIStatusBarStyleLightContent
)。双方UIStatusBarStyleBlackTranslucent
并UIStatusBarStyleBlackOpaque
已被弃用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。
如果有人正在使用导航控制器,并且希望所有导航控制器都具有黑色样式,则可以在Swift 3中像这样向UINavigationController编写扩展,它将扩展应用于所有导航控制器(而不是将其分配给一个导航控制器)。时间)。
extension UINavigationController {
override open func viewDidLoad() {
super.viewDidLoad()
self.navigationBar.barStyle = UIBarStyle.black
}
}
在Swift中用于任何类型的UIViewController:
在您的AppDelegate
集合中:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window!.rootViewController = myRootController
return true
}
myRootController
可以是任何一种UIViewController
,例如UITabBarController
或UINavigationController
。
然后,像这样覆盖此根控制器:
class RootController: UIViewController {
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return .LightContent
}
}
这将更改整个应用程序中状态栏的外观,因为根控制器完全负责状态栏的外观。
请记住,将属性设置View controller-based status bar appearance
为YES Info.plist
即可(默认设置)。
大多数答案都没有包含childViewControllerForStatusBarStyle
方法的良好实现UINavigationController
。根据我的经验,您应该处理诸如在导航控制器上显示透明视图控制器的情况。在这些情况下,您应该将控制权传递给模态控制器(visibleViewController
),而当它消失时则不能。
override var childViewControllerForStatusBarStyle: UIViewController? {
var childViewController = visibleViewController
if let controller = childViewController, controller.isBeingDismissed {
childViewController = topViewController
}
return childViewController?.childViewControllerForStatusBarStyle ?? childViewController
}
至于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];
这是我解决这个问题的方法。
定义一个名为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 +升级会基于其提供的值适当定制。
从Xcode 11.4开始, preferredStatusBarStyle
UINavigationController扩展中属性不再起作用,因为将不会对其进行调用。
设置barStyle
的navigationBar
对.black
作品确实,但如果添加子视图可能有光明和黑暗模式不同出场的导航栏,这将增加不必要的副作用。因为通过将设置barStyle
为黑色,userInterfaceStyle
然后嵌入在navigationBar中的视图的便始终具有与应用程序userInterfaceStyle.dark
无关的视图userInterfaceStyle
。
我想出的正确解决方案是通过添加的子类UINavigationController
并preferredStatusBarStyle
在那里重写。如果然后对所有视图使用此自定义UINavigationController,则将处于保存状态。
NavigationController或TabBarController是需要提供样式的样式。这是我的解决方法:https : //stackoverflow.com/a/39072526/242769