I would like to show and hide the Status bar on some controllers. Can this be done or is it more of an overall app setting.
I have seen many posts/questions about the plist update:
View controller-based status bar appearance - NO
If this is completed what control is then given?
I am looking to show the status bar on the main screen of the application. But for example on a side (slide) menu I would like it not to show, is this possible? Can this be changed in IB or code?
EDIT --
I am using a https://github.com/edgecase/ECSlidingViewController implementation.
The main controller (showing the first page) should show the Status bar, but the left menu controller when it slides should not.
I believe the issue is that they both sit within the same root controller (sliding view controller) so it is difficult to complete.
Ideally if the home screen (main page) could take the status bar with it when it slides that would look best.
The plist setting "View controller-based status bar appearance" only controls if a per-controller based setting should be applied on iOS 7.
If you set this plist option to NO, you have to manually enable and disable the status bar like (as it was until iOS 6):
[[UIApplication sharedApplication] setStatusBarHidden:YES]
If you set this plist option to YES, you can add this method to each of your viewControllers to set the statusBar independently for each controller (which is esp. nice if you have a smart subclass system of viewControllers)
- (BOOL)prefersStatusBarHidden {
return YES;
}
Edit:
there are two more methods that are of interest if you are opting in the new viewController-based status bar appearance -
Force a statusbar update with:
[self setNeedsStatusBarAppearanceUpdate]
If you have nested controllers (e.g. a contentViewController in a TabBarController subclass, your TabBarController subclass might ask it's current childViewController and forward this setting. I think in your specific case that might be of use:
- (UIViewController *)childViewControllerForStatusBarHidden {
return _myChildViewController;
}
- (UIViewController *)childViewControllerForStatusBarStyle {
return _myOtherViewController;
}
On iOS 7 and later, just implement -prefersStatusBarHidden, for example in a UIViewController that should hide the status bar:
- (BOOL)prefersStatusBarHidden {
return YES;
}
The default is NO.
Swift 3:
override var prefersStatusBarHidden: Bool {
return true
}
You can also show/hide the status bar in an animation block, by putting animation code inside didSet property of variable that describes whether it should be shown or hidden. When you set a new value for the statusBarHidden Bool, this automatically triggers the animated updating of the status bar over the duration you have chosen.
/// Swift 3 syntax:
var statusBarHidden: Bool = true {
didSet {
UIView.animate(withDuration: 0.5) { () -> Void in
self.setNeedsStatusBarAppearanceUpdate()
}
}
}
override var prefersStatusBarHidden: Bool {
return statusBarHidden
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
statusBarHidden = false // show statusBar, animated, by triggering didSet block
}
Swift version of Mojo66's answer:
override func prefersStatusBarHidden() -> Bool {
return true
}
Related
I have made following common method for hiding and showing again status bar. It works fine before iOS 13, but I am getting following crash while I run it for device having iOS 13 or greater.
+(void)showStatusBar:(BOOL)show
{
UIView *statusBar = [[[UIApplication sharedApplication] valueForKey:#"statusBarWindow"] valueForKey:#"statusBar"];
if ([statusBar respondsToSelector:#selector(setBackgroundColor:)]) {
[[UIApplication sharedApplication] setStatusBarHidden:!show withAnimation:UIStatusBarAnimationNone];
}
}
Getting following error for iOS 13
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'App called -statusBar or
-statusBarWindow on UIApplication: this code must be changed as there's no longer a status bar or status bar window. Use the
statusBarManager object on the window scene instead.'
What can I do to hide and show status bar for some view controllers only?
If you want to show/hide the status bar on the different View Controllers you need to:
Add View controller-base status bar appearance option in your Info.plist and set it to YES
Override var prefersStatusBarHidden: Bool in each View Controller where you want to have the status bar shown/hidden
override var prefersStatusBarHidden: Bool {
return true
}
If you want to show/hide it dynamically (ex. after tapping on button), you could do something like this:
var statusBarHidden = true {
didSet {
setNeedsStatusBarAppearanceUpdate()
}
}
override var prefersStatusBarHidden: Bool {
return statusBarHidden
}
You could find more verbose explanation here Here
Also in the Apple Documentation for UIStatusBarManager you can find the following quote:
You do not use this object to modify the configuration of the status bar. Instead, you set the status bar configuration individually for each of your UIViewController objects. For example, to modify the default visibility of the status bar, override the prefersStatusBarHidden property of your view controller.
I would like to show and hide the Status bar on some controllers. Can this be done or is it more of an overall app setting.
I have seen many posts/questions about the plist update:
View controller-based status bar appearance - NO
If this is completed what control is then given?
I am looking to show the status bar on the main screen of the application. But for example on a side (slide) menu I would like it not to show, is this possible? Can this be changed in IB or code?
EDIT --
I am using a https://github.com/edgecase/ECSlidingViewController implementation.
The main controller (showing the first page) should show the Status bar, but the left menu controller when it slides should not.
I believe the issue is that they both sit within the same root controller (sliding view controller) so it is difficult to complete.
Ideally if the home screen (main page) could take the status bar with it when it slides that would look best.
The plist setting "View controller-based status bar appearance" only controls if a per-controller based setting should be applied on iOS 7.
If you set this plist option to NO, you have to manually enable and disable the status bar like (as it was until iOS 6):
[[UIApplication sharedApplication] setStatusBarHidden:YES]
If you set this plist option to YES, you can add this method to each of your viewControllers to set the statusBar independently for each controller (which is esp. nice if you have a smart subclass system of viewControllers)
- (BOOL)prefersStatusBarHidden {
return YES;
}
Edit:
there are two more methods that are of interest if you are opting in the new viewController-based status bar appearance -
Force a statusbar update with:
[self setNeedsStatusBarAppearanceUpdate]
If you have nested controllers (e.g. a contentViewController in a TabBarController subclass, your TabBarController subclass might ask it's current childViewController and forward this setting. I think in your specific case that might be of use:
- (UIViewController *)childViewControllerForStatusBarHidden {
return _myChildViewController;
}
- (UIViewController *)childViewControllerForStatusBarStyle {
return _myOtherViewController;
}
On iOS 7 and later, just implement -prefersStatusBarHidden, for example in a UIViewController that should hide the status bar:
- (BOOL)prefersStatusBarHidden {
return YES;
}
The default is NO.
Swift 3:
override var prefersStatusBarHidden: Bool {
return true
}
You can also show/hide the status bar in an animation block, by putting animation code inside didSet property of variable that describes whether it should be shown or hidden. When you set a new value for the statusBarHidden Bool, this automatically triggers the animated updating of the status bar over the duration you have chosen.
/// Swift 3 syntax:
var statusBarHidden: Bool = true {
didSet {
UIView.animate(withDuration: 0.5) { () -> Void in
self.setNeedsStatusBarAppearanceUpdate()
}
}
}
override var prefersStatusBarHidden: Bool {
return statusBarHidden
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
statusBarHidden = false // show statusBar, animated, by triggering didSet block
}
Swift version of Mojo66's answer:
override func prefersStatusBarHidden() -> Bool {
return true
}
How do you hide the status bar in ios 9?
This is now deprecated:
[UIApplication sharedApplication] setStatusBarHidden:YES];
Swift-3
override var prefersStatusBarHidden: Bool {
return true
}
I got the Information From Here
Change func to var
Delete ()
Change -> to :
This works because a computed variable has a getter function, so the function you were implementing before simply turns into the getter function
2016 onwards: simple Thing like
On your info.plist add the following two property for statusBar Hidden
View controller-based status bar appearance (Boolean: NO)
Status bar is initially hidden (Boolean: YES)
By Source
<key>UIStatusBarHidden</key>
<true/>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
or
Old answers ! ...
add application.statusBarHidden in didFinishLaunchingWithOptions
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
application.statusBarHidden = YES;
return YES;
}
and add
in info.plist add this View controller-based status bar appearance set NO
View controller-based status bar appearance = NO
viewcontroller based hidden set
Add method in your view controller.
Objective -C
- (BOOL)prefersStatusBarHidden {
return YES;
}
Swift upto 2
override func prefersStatusBarHidden() -> Bool {
return true
}
(GOOD) 2016.5.17 in iOS 9.0 worked nicely.
Updated Answer
Go to Info.plist file
Hover on one of those lines and a (+) and (-) button will show up.
Click the plus button to add new key
Type in start with capital V and automatically the first choice will be View controller-based status bar appearance. Add that as the KEY.
Set the VALUE to "NO"
Go to you AppDelegate.m for Objective-C (for swift language: AppDelegate.swift)
Add the code, inside the method
For Objective-C:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[application setStatusBarHidden:YES];
return YES;
}
For Swift:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject:AnyObject]?) -> Bool {
application.statusBarHidden = true
return true
}
in info.plist add the following two property.
View controller-based status bar appearance (NO)
Status bar is initially hidden (YES)
I know that the documentation of setStatusBarHidden: does not mention on what use instead. But the header of UIApplication does.
// Setting statusBarHidden does nothing if your application is using the default UIViewController-based status bar system.
#property(readwrite, nonatomic,getter=isStatusBarHidden) BOOL statusBarHidden NS_DEPRECATED_IOS(2_0, 9_0, "Use -[UIViewController prefersStatusBarHidden]");
- (void)setStatusBarHidden:(BOOL)hidden withAnimation:(UIStatusBarAnimation)animation NS_DEPRECATED_IOS(3_2, 9_0, "Use -[UIViewController prefersStatusBarHidden]");
Here is stated that you should use the prefersStatusBarHidden on UIViewController and use view controller based statusbar styles.
All you need to do now is configure whether the view controller needs to show of hide the status bar. Like so :
- (BOOL)prefersStatusBarHidden {
return YES;
}
Here's how do you easily return a control over status bar visibility for iOS 9+ and Swift 3+:
Add View controller-based status bar appearance key with YES value to Info.plist.
Add this variable to the view controller:
private var isStatusBarHidden = false {
didSet {
setNeedsStatusBarAppearanceUpdate()
}
}
Override prefersStatusBarHidden property:
override var prefersStatusBarHidden: Bool {
return isStatusBarHidden
}
That's it. Now you are able to call isStatusBarHidden = true and isStatusBarHidden = false whenever you want.
An easy approach would be to set the windowLevel of the Application to be either normal or statusBar based on your needs, so to start
Objective-C
To Hide the Status Bar
UIApplication.sharedApplication.keyWindow.windowLevel = UIWindowLevelStatusBar;
To Show the Status Bar
UIApplication.sharedApplication.keyWindow.windowLevel = UIWindowLevelNormal;
Also add the Key (View controller-based status bar appearance) with boolean value NO.
If for some reason you need View controller-based status bar appearance equal to YES (for example to keep status bar white)
on AppDelegate's didFinishLaunchingWithOptions method or wherever you setup your navigationController:
yourNavigationController.navigationBar.barStyle = .black
then use alex-staravoitau's awesome answer and add this code wherever you'll be hiding the status bar:
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
I'm not sure if this is the right way to achieve this result, but it worked for me and I hope it helps someone else too :)
In most of the iOS it will work. I have tested with iOS 10.
Open info.plist
"View controller-based status bar appearance" set to NO
"Status bar is initially hidden" set to YES
Done
(iOS 7 Xcode 5.0.2)
I used following methods, successfully change the status bar color to white on root view controller
[self setNeedsStatusBarAppearanceUpdate]; // Update status bar style
-(UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent; // Set status bar color to white
}
Now I'm trying to change status bar color to black when navigate to child view controller, I don't know how to do it.(status bar color is still white)
I searched, and find this method: childViewControllerForStatusBarStyle
I read Apple's document,But still don't know how to/where to use it, and I'm not sure if this is the right approach
Anyone knows how to change status bar color in child view controller?
By default, it seems that UINavigationController unfortunately doesn't provide a sensible default implementation of childViewControllerForStatusBarStyle. By implementing this method, you can tell your navigationController to defer all calls to preferredStatusBarStyle to its topmost childViewController.
You could either subclass UINavigationController and implement the method there, or simply add a category:
#implementation UINavigationController (ChildStatusBarStyle)
- (UIViewController *)childViewControllerForStatusBarStyle
{
return self.topViewController;
}
#end
I just find out:
When you embed the root view controller inside UINavigationController correctly, You'd never need to create a category to expand the capability of navigation controller, or subclassing UINavigationController for the same purpose.
You just need to put preferredStatusBarStyle inside every view controller, and remember to call [self setNeedsStatusBarAppearanceUpdate]; to update status bar style. Simple as it is!
check out this video from WWDC 2013: Click Here
EDIT:
The reason I made it working, is I happen to set UINavigationBar hidden. In this case, it behaves the same when not using UINavigationController at all.
When you Trying to change StatusBarStyle of an UIViewController which is inside UINavigationController stack. It will fail to work in this way. It only works in individual UIViewController. The WWDC 2013 Video example is not using UINavigationController, so that why the approach is working fine.
James Frost answer was the only one that worked for me. Thank you! Here's a Swift 4 version of that code.
extension UINavigationController {
override open var childViewControllerForStatusBarStyle: UIViewController? {
return topViewController
}
}
Note: this is a bit unwieldy as-is, I recommend adding some code to limit its scope to a single viewController. Something like this:
extension UINavigationController {
override open var childViewControllerForStatusBarStyle: UIViewController? {
if topViewController is MyViewController {
return topViewController
} else {
return nil
}
}
}
You'll obviously need to replace MyViewController with your UIViewController subclass that implements preferredStatusBarStyle.
override var preferredStatusBarStyle: UIStatusBarStyle {
if isBackgroundDark() {
return .lightContent
} else {
return .default
}
}
Again isBackgroundDark() is yours to implement.
Finally don't forget to call setNeedsStatusBarAppearanceUpdate() in your viewController every time isBackgroundDark() changes its value.
In contrast to what James Frost said, and after much time spent debugging why my Browser Activities had wrong StatusBar colors (Swift):
override func childViewControllerForStatusBarStyle() -> UIViewController? {
return visibleViewController
}
That said: In some scenarios .topViewController is right, in others like with UIActivities it's .visibleViewController.
Thanks to #James Frost, the solution works well.
I didn't make it work at first, so I want to make a further explanation about it.
If you have a subclass of UINavigationController,
it's important to add preferredStatusBarStyle in your UINavigationController subclass at the same time.
- (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent;
}
And add childViewControllerForStatusBarStyle in extension of UINavigationController.
extension UINavigationController {
override open var childViewControllerForStatusBarStyle: UIViewController? {
return visibleViewController
}
}
BTW, it's OK that UINavigationController subclass and extension of UINavigationController use different coding language, still work.
I've tried another solutions and noticed that the Status Bar doesn't update without setNeedsStatusBarAppearanceUpdate(). There can be multiple places to call this marker, but the easies way is to override viewControllers setter.
class StatusBarNavigationController: UINavigationController {
override var childForStatusBarHidden: UIViewController? {
return topViewController
}
override var viewControllers: [UIViewController] {
didSet { setNeedsStatusBarAppearanceUpdate() }
}
}
Then you can use the StatusBarNavigationController programmatically or on a Storyboard.
I need to let a specific ViewController embedded in an UINavigationController to have light status bar text color (but other ViewControllers to behave differently). I am aware of at least 3 methods, none of which however work in my case.
How to change Status Bar text color in iOS 7, the method is primarily:
Set the UIViewControllerBasedStatusBarAppearance to YES in the plist
In viewDidLoad do a [self setNeedsStatusBarAppearanceUpdate];
Add the following method:
- (UIStatusBarStyle)preferredStatusBarStyle{
return UIStatusBarStyleLightContent;
}
Running on iOS 7.0.3, this method does not work for me, since even after I have implemented all 3 steps correctly, preferredStatusBarStyle is never called.
UIStatusBarStyle PreferredStatusBarStyle does not work on iOS 7, the method is primarily:
Setting your navigationBar’s barStyle to UIBarStyleBlackTranslucent will give white status bar text (ie. UIStatusBarStyleLightContent), and UIBarStyleDefault will give black status bar text (ie. UIStatusBarStyleDefault).
This method works fair and square on iPhone, but not on iPad.
Setting the UIViewControllerBasedStatusBarAppearance to NO in the plist, and use
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
This clearly doesn't apply in this case, since I need to only specify different status bar colors for two of the ViewControllers.
Thanks for all help!
For people having this problem with a UINavigationController I can recommend creating a custom UINavigationController and implementing the preferredStatusBarStyle on it like this:
- (UIStatusBarStyle)preferredStatusBarStyle
{
return [self.topViewController preferredStatusBarStyle];
}
That way the statusbar style will be that of the top view controller. Now you can implement the view controller's preferredStatusBarStyle anyway you like.
Here's an improvement to Groot answer, in form of a simple category to UINavigationController, without the need to subclass UINavigationController.
Swift
extension UINavigationController {
override public func preferredStatusBarStyle() -> UIStatusBarStyle {
return topViewController?.preferredStatusBarStyle() ?? .Default
}
}
Swift 3 & Swift 4
extension UINavigationController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
return topViewController?.preferredStatusBarStyle ?? .default
}
}
Objective-C
#implementation UINavigationController (StatusBarStyle)
- (UIStatusBarStyle)preferredStatusBarStyle
{
return [self.topViewController preferredStatusBarStyle];
}
#end
To set UIStatusBarStyle individually for each UIViewController on UINavigationController stack you have to first subclass your UINavigationController and override childViewControllerForStatusBarStyle method.
In your UINavigationController subclass add:
-(UIViewController *)childViewControllerForStatusBarStyle {
return self.visibleViewController;
}
than you can set UIStatusBarStyle to whatever you want in every UIViewController using preferredStatusBarStyle method. Eg:
-(UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleLightContent;
}
iOS 13 Solution(s)
Regarding your attempt #3 - DEPRECATED
UIApplication.setStatusBarStyle(_:animated:) has been deprecated since iOS 9. According to Apple,
In iOS 7 and later, status bar behavior is determined by view controllers, and so calling this method has no effect by default. When view controller-based status bar appearance is disabled, this method behaves normally. To opt out of the view controller-based status bar appearance behavior, you must add the UIViewControllerBasedStatusBarAppearance key with a value of false to your app’s Info.plist file, but doing so is not recommended.
Regarding your attempt #2 - LEGACY
Setting the barStyle property is now (iOS 13+) considered a "legacy customization." According to Apple,
In iOS 13 and later, customize your navigation bar using the standardAppearance, compactAppearance, and scrollEdgeAppearance properties. You may continue to use these legacy accessors to customize your navigation bar's appearance directly, but you must update the appearance for different bar configurations yourself.
Regarding your attempt #1 - You were on the right track!
UINavigationController is a subclass of UIViewController (who knew 🙃)!
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.
Any of the following methods should work:
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.
P.S. My code uses Swift 5.1 syntax 😎
I used the first method you mentioned, I also found there's kinda bug when you used UINavigationController, it will never pass preferredStatusBarStyle call to it's child view controllers. What I have done is subclass the UINavigationController, and override preferredStatusBarStyle method as follows:
#implementation GLBaseNavigationController
- (UIStatusBarStyle)preferredStatusBarStyle
{
UIViewController *lastViewController = [self.viewControllers lastObject];
if ([lastViewController respondsToSelector:#selector(preferredStatusBarStyle)]) {
return [lastViewController preferredStatusBarStyle];
} else if ([super respondsToSelector:#selector(preferredStatusBarStyle)]) {
return [super preferredStatusBarStyle];
}
return UIStatusBarStyleDefault;
}
Then whenever I need a navigation controller, I use GLBaseNavigationController instead of UINavigationController. For storyboards, you need to specify the class of the navigation controller to your subclass as well.
For your first solution, I don't think you can change the status bar in viewDidLoad. If you have two ViewControllers stacked on top of each other, and each one toggles the status bar differently, that method will only get called once for each. You really want to change the status bar in viewWillAppear so that it gets called each time the page is shown. I also don't think you can rely on preferredStatusBarStyle since I'm also not sure how often/when that gets called. This is how you want to do it:
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController.navigationBar setBarStyle:UIBarStyleDefault];
}
Currently you can only do light and dark. To change to light do.
Set the UIViewControllerBasedStatusBarAppearance to YES in the .plist file.
In the viewDidLoad method do [self setNeedsStatusBarAppearanceUpdate];
Add the this method:
-(UIStatusBarStyle)preferredStatusBarStyle{
return UIStatusBarStyleLightContent;
}
To change it back to dark change the UIStatusBarStyleLightContent to UIStatusBarStyleDefault
In your AppDelegate didFinishLaunch method, set the default status bar style, say:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault
animated:YES];
return YES;
}
Then, in your those two view controllers, where you want to change status bar, override following methods:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated]
// Here change status bar color
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent
animated:YES];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear]
// Here bring back to color, that we set in AppDelegate
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault
animated:YES];
}