我遵循此线程进行覆盖-preferredStatusBarStyle
,但未调用它。我可以更改任何选项来启用它吗?(我在项目中使用XIB。)
可能的根本原因
我遇到了同样的问题,并且发现了这种情况的发生是因为我没有在应用程序窗口中设置根视图控制器。
的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
。
对于使用UINavigationController的任何人:
该UINavigationController
不转发就preferredStatusBarStyle
调用它的子视图控制器。相反,它管理自己的状态-应当在状态栏所在的屏幕顶部绘制图形,因此应该对此进行管理。因此preferredStatusBarStyle
,在nav控制器中的VC中实现将不会执行任何操作-永远不会调用它们。
诀窍是UINavigationController
用来决定要返回UIStatusBarStyleDefault
或的内容的方式UIStatusBarStyleLightContent
。它基于它UINavigationBar.barStyle
。默认值(UIBarStyleDefault
)会导致前景UIStatusBarStyleDefault
状态栏变暗。并UIBarStyleBlack
会给出一个UIStatusBarStyleLightContent
状态栏。
TL; DR:
如果你想UIStatusBarStyleLightContent
在UINavigationController
使用:
self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
因此,我实际上向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的默认设置吗?)
对于仍然为此感到困扰的任何人,迅速的此简单扩展都应为您解决问题。
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上,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
}
}
对于将中的状态栏颜色更改为白色,泰森的答案是正确的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;
}
除了serenn的答案外,如果要为视图控制器提供一个modalPresentationStyle
(例如.overCurrentContext
),还应该在新显示的视图控制器上调用它:
presentedViewController.modalPresentationCapturesStatusBarAppearance = true
别忘了还要覆盖preferredStatusBarStyle
显示的视图控制器中的。
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,以便呈现的视图控制器必须接收状态栏外观控件。
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
该解决方案可能比切换到即将弃用的行为更好。
iOS 13解决方案
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)-当系统需要视图控制器来确定状态栏样式时调用- 根据Apple的文件,
“如果您的容器视图控制器从其子视图控制器之一派生其状态栏样式,请[覆盖此属性]并返回该子视图控制器。如果您返回nil或不重写此方法,则使用self的状态栏样式如果此方法的返回值发生更改,请调用setNeedsStatusBarAppearanceUpdate()方法。”
- 换句话说,如果您在此处未实现解决方案3,则系统将退回到上述解决方案2。
-
子类或扩展
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
}
ViewControllerpreferredStatusBarStyle
将被调用。
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
}
}
就我而言,我不小心将View / Navigation Controller显示为UIModalPresentationStyle.overFullScreen
,这导致preferredStatusBarStyle
未调用它。将其切换回之后UIModalPresentationStyle.fullScreen
,一切正常。
在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
为YESInfo.plist
即可(默认设置)。
Swift 3 iOS 10解决方案:
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
大多数答案都没有包含childViewControllerForStatusBarStyle
for的方法的良好实现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
,似乎不需要使用子类才是唯一的选择。
例:
类别标题:
@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 +升级会基于其提供的值适当定制。
如果有人用UISearchController遇到此问题。只需创建UISearchController的新子类,然后将以下代码添加到该类中:
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return .LightContent
}
请注意,使用self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
解决方案时
确保转到您的列表,并将“基于视图控制器的状态栏外观”设置为“是”。如果否,将不起作用。
从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
文章标签:ios , ios7 , uikit , uistatusbar
版权声明:本文为原创文章,版权归 admin 所有,欢迎分享本文,转载请保留出处!
评论已关闭!