iOS 13 Solution(s)
UINavigationController
is a subclass of UIViewController
! (who knew 🙃)
UINavigationController是UIView控制器的子类!(谁知道🙃)
Therefore, when presenting view controllers embedded in navigation controllers, you're not really presenting the embedded view controllers; you're presenting the navigation controllers! UINavigationController
, as a subclass of UIViewController
, inherits preferredStatusBarStyle
and childForStatusBarStyle
, which you can set as desired.
因此,当呈现嵌入在导航控制器中的视图控制器时,您实际上并没有呈现嵌入的视图控制器;您呈现的是导航控制器!UINavigationController作为UIView控制器的子类,继承了您可以根据需要设置的首选项StatusBarStyle和Child ForStatusBarStyle。
Any of the following methods should work:
以下任何一种方法都应该有效:
Opt out of Dark Mode entirely
- In your
info.plist
, add the following property:
- Key -
UIUserInterfaceStyle
(aka. "User Interface Style")
- Value - Light
Override preferredStatusBarStyle
within UINavigationController
preferredStatusBarStyle
(doc) - The preferred status bar style for the view controller
Subclass or extend UINavigationController
class MyNavigationController: UINavigationController {
override var preferredStatusBarStyle: UIStatusBarStyle {
.lightContent
}
}
OR
extension UINavigationController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
.lightContent
}
}
Override childForStatusBarStyle
within UINavigationController
childForStatusBarStyle
(doc) - Called when the system needs the view controller to use for determining status bar style
- According to Apple's documentation,
"If your container view controller derives its status bar style from one of its child view controllers, [override this property] and return that child view controller. If you return nil or do not override this method, the status bar style for self is used. If the return value from this method changes, call the setNeedsStatusBarAppearanceUpdate() method."
In other words, if you don't implement solution 3 here, the system will fall back to solution 2 above.
Subclass or extend UINavigationController
class MyNavigationController: UINavigationController {
override var childForStatusBarStyle: UIViewController? {
topViewController
}
}
OR
extension UINavigationController {
open override var childForStatusBarStyle: UIViewController? {
topViewController
}
}
You can return any view controller you'd like above. I recommend one of the following:
topViewController
(of UINavigationController
) (doc) - The view controller at the top of the navigation stack
visibleViewController
(of UINavigationController
) (doc) - The view controller associated with the currently visible view in the navigation interface (hint: this can include "a view controller that was presented modally on top of the navigation controller itself")
Note: If you decide to subclass UINavigationController
, remember to apply that class to your nav controllers through the identity inspector in IB.
注意:如果您决定将UINavigationControler子类化,请记住通过IB中的身份检查器将该类应用于您的NAV控制器。
Edits: Strikethrough edits were made to remove extensions as a suggested answer. Other developers noted that they stopped working in Xcode 11.4 and Apple's documentation discourages the use of this ambiguous behavior.
编辑:删除线编辑是为了删除作为建议答案的扩展。其他开发人员指出,他们在Xcode 11.4中停止了工作,Apple的文档不鼓励使用这种模棱两可的行为。
P.S. My code uses Swift 5.1 syntax 😎
附注:我的代码使用SWIFT 5.1语法😎
If you set the UIViewControllerBasedStatusBarAppearance
key in your app's info.plist
to YES
, you can override the status bar style in your currently presented view controller:
如果您在应用程序的info.plist中将UIViewControllerBasedStatusBarspections键设置为yes,则可以覆盖当前呈现的视图控制器中的状态栏样式:
override var preferredStatusBarStyle: UIStatusBarStyle {
if #available(iOS 13, *) {
return .darkContent
} else {
return .default
}
}
and call the setNeedsStatusBarAppearanceUpdate() method
并调用setNeedsStatusBarEmeraranceUpdate()方法
You can write extension to UIStatusBarStyle
:
您可以向UIStatusBarStyle写入扩展名:
extension UIStatusBarStyle {
static var black: UIStatusBarStyle {
if #available(iOS 13.0, *) {
return .darkContent
}
return .default
}
}
And then you can easily use in your ViewControllers:
然后,您可以轻松地在您的ViewController中使用:
override var preferredStatusBarStyle: UIStatusBarStyle {
.black
}
You can try to make your navigation bar always light
您可以尝试使导航栏始终亮起
if #available(iOS 13.0, *) {
navigationController?.navigationBar.overrideUserInterfaceStyle = .light
}
I have done something like this.
我曾经做过这样的事情。
I have toggler function wich toggles Status bar style depending on View Controller displayed
我有切换功能,可以根据显示的视图控制器来切换状态栏样式
func toggleLight() {
self.navigationBar.barTintColor = AppColors.White
isDarkStyle = false
setNeedsStatusBarAppearanceUpdate()
}
and here is most important part
这是最重要的部分
override var preferredStatusBarStyle: UIStatusBarStyle {
if #available(iOS 13.0, *) {
return isDarkStyle ? .lightContent : .darkContent
}
return isDarkStyle ? .lightContent : .default
}
Where isDarkStyle represents navigation bar background colour dark or light. If it is dark then text (content) should be light, if it is light than text (content) should be default or from iOS 13 dark.
其中,isDarkStyle表示导航栏的背景色、深色或浅色。如果它是深色的,那么文本(内容)应该是浅色的,如果它是浅色的,那么文本(内容)应该是默认的或来自iOS 13的深色。
To sum up: .lightContent, .darkContent
displays independent of Dark Mode as it is supposed to do. While .default
is susceptible to Dark Mode changes!
总而言之:.lightContent,.darkContent独立于黑暗模式显示,就像它应该做的那样。而.Default易受黑暗模式更改的影响!
If you're using a UINavigationController
, override the preferredStatusBarStyle
in an extension (or your own subclass) like this (just overriding preferredStatusBarStyle
in your view controllers won't work):
如果您使用的是UINavigationController,那么在一个扩展(或您自己的子类)中重写operredStatusBarStyle,如下所示(只是重写视图控制器中的preciredStatusBarStyle将不起作用):
extension UINavigationController {
override open var preferredStatusBarStyle: UIStatusBarStyle {
guard #available(iOS 13, *) else {
return .default
}
return .darkContent
}
}
And as Frank said, UIViewControllerBasedStatusBarAppearance
must be set to YES
in your info.plist
正如Frank所说,必须在info.plist中将UIViewControllerBasedStatusBarance设置为yes
There are two ways to solve this problem. Define the childViewControllerForStatusBarStyle function in the UINavigationController descendant class:
有两种方法可以解决这个问题。在UINavigationController子类中定义子ViewControllerForStatusBarStyle函数:
@interface MyNavigationController : UINavigationController
...
@end
@implementation MyNavigationController
...
- (UIViewController *)childViewControllerForStatusBarStyle
{
return self.topViewController;
}
...
@end
After that, you need to add the function preferredStatusBarStyle for each controller.
在此之后,您需要为每个控制器添加首选项StatusBarStyle函数。
The second option is to define the preferredStatusBarStyle function for all controllers. But this function should not be located in the root controller, but in the descendant class of the UINavigationController.
第二个选项是为所有控制器定义首选状态BarStyle函数。但此函数不应位于根控制器中,而应位于UINavigationController的后代类中。
@interface MyNavigationController : UINavigationController
...
@end
@implementation MyNavigationController
...
- (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent;
}
...
@end
However, even in this case, it is necessary to define the function preferredStatusBarStyle for all controllers that hide the navigation bar (if any).
但是,即使在这种情况下,也需要为隐藏导航栏(如果有的话)的所有控制器定义函数preciredStatusBarStyle。
This extension helps you to change the status bar text color and support also iOS 13 https://stackoverflow.com/a/59767435/10512612
此扩展可帮助您更改状态栏文本颜色,并支持iOS 13 https://stackoverflow.com/a/59767435/10512612
If anyone else finds that neither of the above answers work (as I did), there's a very specific edge-case if you have multiple windows within a UIWindowScene
where it will use the topmost window rather than the key window to determine status bar appearance.
如果其他人发现上面的两个答案都不起作用(就像我一样),那么就有一个非常特殊的边缘情况--如果你在一个UIWindowScene中有多个窗口,它将使用最上面的窗口而不是键窗口来确定状态栏的外观。
So for example, if you have your key window with a windowLevel
set to 1
and a secondary window with a windowLevel
set to 1.1
, UIKit will (for whatever reason) ask the view controllers in your secondary window for their status bar appearance rather than the ones in the key window, even though its alpha is 0.
因此,例如,如果关键点窗口的windowLevel设置为1,而辅助窗口的windowLevel设置为1.1,则UIKit将(无论出于何种原因)向辅助窗口中的视图控制器询问其状态栏外观,而不是询问关键点窗口中的控制器,即使其Alpha为0。
Our workaround was simply to only put the secondary window on a higher level when it was active, and lowering it below the key window when it was hidden.
我们的解决方法只是在辅助窗口处于活动状态时将其置于较高级别,而在隐藏时将其降低到关键点窗口下方。
Add the below two lines in info.plist
在info.plist中添加以下两行
Key: Appearance , Type: string , Value: (dark/light)
Key: View controller-based status bar appearance, Type: Boolean, Value: YES
then above the top status bar(Time shows above) will be in light mode.
然后,顶部状态栏(上面的时间显示)的上方将处于灯光模式。
Or copy the below code and paste in info.plist(right click on info.plist and open with Source Code)
或者复制以下代码并粘贴到info.plist中(右键单击info.plist并打开源代码)
<key>UIUserInterfaceStyle</key>
<string>light</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
更多回答
Note that the extension method of doing override var does not work anymore in Xcode 11.4/iOS 13.4
请注意,执行覆盖var的扩展方法在Xcode11.4/iOS 13.4中不再有效
@MarcEtcheverry do you know what the alternative is for 13.4?
@MarcEtcheverry你知道13.4的替代方案是什么吗?
Subclass any of those container view controllers and override there, which is the proper way to do this. Swift extensions for ObjC classes may be implemented using ObjC categories, which never could safely override things. You could swizzle it in ObjC, but subclassing is safer.
将任何容器视图控制器子类化并在那里覆盖,这是完成此操作的正确方法。可以使用Objc类别实现对Objc类的SWIFT扩展,而Objc类别永远不能安全地覆盖对象。您可以在objc中使用它,但子类化更安全。
"If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime. This is less likely to be an issue if you’re using categories with your own classes, but can cause problems when using categories to add methods to standard Cocoa or Cocoa Touch classes". developer.apple.com/library/archive/documentation/Cocoa/…
如果在类别中声明的方法的名称与原始类中的方法相同,或者与同一类(甚至超类)上的另一个类别中的方法相同,则行为未定义在运行时使用哪个方法实现。如果将类别与自己的类一起使用,则不太可能出现问题,但当使用类别向标准Cocoa或Cocoa Touch类添加方法时,可能会导致问题。Developer.apple.com/library/archive/documentation/Cocoa/…
I worked for Darkmode (have to make it same as light mode as per Client requirement) then after finished my work just saw this that we can exclude support for darkmode just with a keyword! :D good for next time atleast..!
我为黑暗模式工作(必须根据客户的要求使其与灯光模式相同),然后在完成我的工作后,我发现我们可以通过一个关键字来排除对黑暗模式的支持!:D至少下一次很好..!
I am using both of those configurations but Dark Mode still overrides the status bar color.
我正在使用这两种配置,但深色模式仍然覆盖状态栏颜色。
@AndrewKirna Hard so say what the issue might be. Do you maybe want to post a question for that?
@AndrewKirna Hard,所以说出问题可能是什么。你想不想发布一个关于这个问题的问题?
Ok, I’ll consider posting. It seems Apple wants me to use a dynamic color for my nav bars, however, I don’t want to do that because it uses my company’s branding. I’m guessing your solution has the same problem I’m experiencing (black status bar text in light mode; white status bar text in dark mode) despite overriding preferredStatusBarStyle
in each view controller. Have you built it on Xcode 11 for iOS 13?
好的,我会考虑发帖的。苹果似乎希望我的导航栏使用动态的颜色,然而,我不想这样做,因为它使用了我公司的品牌。我猜您的解决方案具有与我相同的问题(亮模式下的黑色状态栏文本;暗模式下的白色状态栏文本),尽管覆盖了每个视图控制器中的首选状态栏样式。你是在iOS13的Xcode11上构建的吗?
Yes, works perfectly for me. Are you sure you override it in the main presented view controller?
是的,对我来说很合适。您确定要在主显示的视图控制器中覆盖它吗?
Frank, I followed a bunch of links related to your link above and eventually discovered what I needed! Thanks for the help. I posted my solution below!
弗兰克,我关注了一堆与你上面的链接相关的链接,最终发现了我需要的东西!谢谢你的帮助。我在下面发布了我的解决方案!
我是一名优秀的程序员,十分优秀!