How to hide, show or change the status bar dynamically on events such as click or value change?
I've googled for a well, but all I get is changed from the beginning as this. Here I need to do it dynamically.
Well for this you can do following based on events:
//For hiding/unhiding:
func hideStatusBar(shouldHide:Bool){
UIApplication.shared.isStatusBarHidden = shouldHide
}
//For Light Style:
func lightStatusBar(){
UIApplication.shared.statusBarStyle = .lightContent
}
//For Standard Style:
func standardStatusBar(){
UIApplication.shared.statusBarStyle = .default
}
or you can tweak prefersStatusBarHidden: as well.
UIApplication.sharedApplication().setStatusBarHidden=true
You are looking for var prefersStatusBarHidden: Bool { get }
Override that function in your view controller. Dynamically update the visibility of your statusbar with func setNeedsStatusBarAppearanceUpdate()
https://developer.apple.com/reference/uikit/uiviewcontroller/1621440-prefersstatusbarhidden
Related
I am building an app for iOS13. I want to change the text color of status bar according to the color scheme which user can select. It seems the text color of status bar cannot be changed directly, so I try to change the statusBarStyle instead.
First I try:
var myStatusBarStyle = UIStatusBarStyle.default { didSet { setNeedsStatusBarAppearanceUpdate() } }
override var preferredStatusBarStyle : UIStatusBarStyle { return myStatusBarStyle; }
But it did not work.
Then I try:
UIApplication.shared.statusBarStyle = myStatusBarStyle;
It does do the job! But Xcode gives a warnning: " 'statusBarStyle' was deprecated in iOS 13.0: Use the statusBarManager property of the window scene insted."
so, in sceneWillEnterForeground(_ scene: UIScene) I try:
guard let ws = (scene as? UIWindowScene) else { return }
if let t = ws.statusBarManager { print(t.statusBarStyle) }
Unfortunately, t.statusBarStyle is readonly.
Now, how can I do?
ps: I have no Navigation Bar or Tab Bar in the Main.storyboard.
in iOS 13, we are not allowed to do that but there are always escape roots..
I used
self.navigationController?.navigationBar.barStyle = .black
To make status bar black and it works
You can use various options instead of black as you like (.default , .blackTranslucent , .blackOpaque) in place of .black
I have a view controller that takes up the whole screen from top to bottom. I would like to hide the home bar indicator on the bottom of the screen on iPhone X devices.
How can I do this in iOS 11?
You should override prefersHomeIndicatorAutoHidden in your view controller to achieve that:
override var prefersHomeIndicatorAutoHidden: Bool {
return true
}
There is another alternative. If you are looking for the behavior where the indicator dims, then when the user swipes up it activates, and when they swipe up again the home action is invoked (I.E., two swipes are needed to invoke), then the answer is here: iPhone X home indicator behavior. The short of it is to override on your UIViewController:
override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge {
return UIRectEdge.bottom
}
prefersHomeIndicatorAutoHidden only hides the indicator, but will not suppress the gesture.
And you will get what you want (If I understand your comments correctly - your question and the selected answer seem to imply the other answer).
If your window?.rootViewController is a UITabBarController or UINavigationController, just inherit it and add two function as follows,
override var prefersHomeIndicatorAutoHidden: Bool {
return true
}
//#available(iOS 11, *)
override var childViewControllerForHomeIndicatorAutoHidden: UIViewController? {
return nil
}
Implement -(BOOL)prefersHomeIndicatorAutoHidden in your UIViewController and return YES.
Read more https://developer.apple.com/documentation/uikit/uiviewcontroller/2887510-prefershomeindicatorautohidden.
I tried to set it and return true only when I am in full-screen :
override var prefersHomeIndicatorAutoHidden: Bool { isNavigationBarAndTabBarHidden }
but it doesn't seem to work... isNavigationBarAndTabBarHidden is a custom variable tied to my fullscreen extension.
Edit: We need to call setNeedsUpdateOfHomeIndicatorAutoHidden every time we update prefersHomeIndicatorAutoHidden's value.
var isNavigationBarAndTabBarHidden = false {
didSet {
setNeedsUpdateOfHomeIndicatorAutoHidden()
}
}
override func prefersHomeIndicatorAutoHidden() -> Bool {
return true
}
I suppose you can add this method in your AppDelegate for hide home indicator on all of your ViewControllers.
I have an inputAccessoryView for text input in a chat app. I implemented the inputAccessoryView using the following:
override var inputAccessoryView: UIView? {
get {
setupInputToolbar()
return inputToolbar
}
}
override var canBecomeFirstResponder: Bool {
return true
}
This occurs in a viewController which is a childViewController. I use a segmented control to display this chat viewController with the inputAccessoryView.
The problem I'm having is the inputAccessoryView will only display if I place self.becomeFirstResponder() in the viewDidAppear() of the chat viewController (child).
If I omit self.becomeFirstResponder() or place it in viewDidLoad() or viewWillAppear(), the inputAccessoryView does not display.
The problem with having it in viewDidAppear() is that it displays with an animation after the view is already on screen which is not what I want.
I accomplished this by first adding a viewHasPerformedSubviewLayoutAtLeastOnce property to the ViewController.
private var viewHasPerformedSubviewLayoutAtLeastOnce = false
Making canBecomeFirstResponder dependent on the above property.
override var canBecomeFirstResponder: Bool {
return viewHasPerformedSubviewLayoutAtLeastOnce
}
Then updating that property in viewDidLayoutSubviews() and becoming first responder inside a UIView.performWithoutAnimation block.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if viewHasPerformedSubviewLayoutAtLeastOnce == false {
viewHasPerformedSubviewLayoutAtLeastOnce = true
UIView.performWithoutAnimation { becomeFirstResponder() }
}
}
It all feels a little hacky, but it results in the view appearing without any animations.
I cannot seem to be able to hide the status bar in my root view controller (A) when a button is pressed.
I have set the properties in my info.plist Status bar is initially hidden and View controller-based status bar appearance to YES.
If I implement the override var prefersStatusBarHidden: Bool { get }, the status bar will be definitively hidden (or not).
What I need
I want the status bar to be displayed in (A) but hidden when I press a button that adds a child view controller to (A).
I have tried setting prefersStatusBarHidden to false and hide it using UIApplication.shared.isStatusBarHidden = true when the button is pressed but this does not work.
There must be something I am getting wrong, could anyone enlighten me ?
Thanks in advance.
PS: I merely need it to be invisible, not necessarily "hidden" in the Swift sense.
To hide the status bar
UIApplication.shared.keyWindow?.windowLevel = UIWindowLevelStatusBar
To bring back the status bar
UIApplication.shared.keyWindow?.windowLevel = UIWindowLevelNormal
You can create a variable to hold a boolean to set if the status bar is hidden or not
var _isStatusBarHidden: Bool = false
Then create its getter and setter
var isStatusBarHidden: Bool {
get{
return _isStatusBarHidden
}
set {
if _isStatusBarHidden != newValue {
_isStatusBarHidden = newValue
self.setNeedsStatusBarAppearanceUpdate()
}
}
}
You must override the prefersStatusBarHidden property
override var prefersStatusBarHidden: Bool {
return self._isStatusBarHidden
}
Last thing is to set the variable to true when the user presses the button:
self.isStatusBarHidden = true
Just a little improvement of #Alexandre Lara `s code:
var isStatusBarHidden: Bool = false {
didSet {
if oldValue != self.isStatusBarHidden {
self.setNeedsStatusBarAppearanceUpdate()
}
}
}
override var prefersStatusBarHidden: Bool {
return self.isStatusBarHidden
}
I am implementing a dark mode in my app. Here is my code (that I call when the screen is double tapped):
if darkMode == false {
UINavigationBar.appearance().tintColor = UIColor(hexString: "#3A3A3A")
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName:UIColor.whiteColor()]
UIApplication.sharedApplication().statusBarStyle = UIStatusBarStyle.LightContent
} else {
UIApplication.sharedApplication().statusBarStyle = UIStatusBarStyle.Default
UINavigationBar.appearance().barTintColor = UIColor(hexString: "#FFFDF3")
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName:UIColor.blackColor()]
The only thing that updates is my status bar, but the navigation bar does update after I go into another view and return back to the main view. Why is that? Is there something I'm doing wrong?
I was just dealing with the same issue, turns out if you change appearance() proxy at runtime it doesn't have any effect. You need to change directly the properties of instances. So what you need to do is have subclassed UINavigationBarController with method where you set the colors and status bar appearance, for instance:
class ColorNavigationController: UINavigationController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
setupForColor(UIFactory.sharedInstance.tintColor) //provides default color
}
func setupForColor(color: UIColor) {
navigationBar.tintColor = color
navigationBar.titleTextAttributes = [NSForegroundColorAttributeName:UIColor.whiteColor()]
UIApplication.sharedApplication().statusBarStyle = UIStatusBarStyle.LightContent
}
}
Then when you double tap the screen:
if let colorNavigationController = self.navigationController as? ColorNavigationController {
colorNavigationController.setupForColor(UIColor.redColor) // based on your current scheme
}
Got it. You can't change appearance() at runtime, but you can just do navigationController?.navigationBar.tintColor = UIColor.redColor()