I want to edit the status bar style of my application. However, I'm unable to edit it from my main UIViewController. So I think multiple status bars settings are stacked because of embed UIViewControllers.
Here is how I initiate my navigationController in the didFinishLaunchingWithOptions method:
let navigationVC = CustomNavigationController(rootViewController: MenuInstance)
navigationVC.setNavigationBarHidden(true, animated: false)
Then, I move some views of other UIViewControllers in the menu UIViewController (MenuInstance) for any reason like this:
let scannerVC = ScannerViewController()
override func viewDidLoad() {
super.viewDidLoad()
addChildViewController(scannerVC)
scannerVC.didMove(toParentViewController: self)
}
I tried to create class to set prefersStatusBarHidden = true
class ModalViewViewController: UIViewController {
override var prefersStatusBarHidden: Bool {
get {
return true
}
}
override func viewDidLoad() {
super.viewDidLoad()
setNeedsStatusBarAppearanceUpdate()
}
}
I also created a class for the UINavigationController
class CustomNavigationController: UINavigationController {
override var prefersStatusBarHidden: Bool {
get {
return true
}
}
override func viewDidLoad() {
super.viewDidLoad()
setNeedsStatusBarAppearanceUpdate()
}
}
The goal of trying to remove the status bar is to find where the status bar comes from. I want only one status bar in the MenuInstance UIViewController that I can edit. The fact that some views of UIViewControllers are embed in one UIViewController makes me confused.
If the MenuInstance view controller is the root view controller of the navigation controller, then the MenuInstance's implementation of prefersStatusBarHidden is all that matters. No other view controller's preference is consulted. The "embed" stuff is irrelevant (unless you want to make it relevant).
class MenuInstance : UIViewController {
override var prefersStatusBarHidden: Bool {
get {
return true
}
}
}
Note, however, that this will not work on an iPhone X. You cannot hide the status bar on an iPhone X.
Related
I am wondering how i can set the nav bar color for current VC only, without using viewWillAppear and viewWillDisappear pairs. These pair of functions are hard to maintain because it split the logic into 2 places.
I have seen multiple SO answers, but they change the whole navigation bar item throughout all VCs in the nav stack.
Two different solutions comes to my mind.
Implement custom NavigationBar for that ViewController
Use a base ViewController to override NavigationBar color in each controller
I would prefer the second way, and here is an example:
class BaseViewController: UIViewController {
// Set one color as default and override this property in each view controller that you want to change navigation bar color
var navigationBarColor: UIColor {
return .red
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.barTintColor = navigationBarColor
}
}
class FirstViewController: BaseViewController {
override var navigationBarColor: UIColor {
return .blue
}
// Do your stuff
}
class SecondViewController: BaseViewController {
override var navigationBarColor: UIColor {
return .green
}
// Do your stuff
}
Hi everyone I'm trying to hide my statusBar in a View Controller but it doesn't seem to work .. I used the function:
override var prefersStatusBarHidden: Bool {
return true
}
I also set the View controller-based status bar appearance in the plist file to YES
My status bar doesn't want to hide ... where am I doing wrong?
It looks like you're trying to specifically hide the status bar in a single ViewController.
In order to do that, you need have the following in that ViewController
self.modalPresentationCapturesStatusBarAppearance = true
override var prefersStatusBarHidden: Bool {
return true
}
I also added View controller-based status bar appearance in my .plist and set it to YES.
Tested on the latest iOS 13.
If the target view controller is embedded in another container view controller such as UINavigationController you need to subclass that container view controller and override its childForStatusBarHidden to return the target view controller.
you kan look this,you should override childForStatusBarHidden and childForStatusBarStyle 。
class CCNavigationController: UINavigationController {
override var childForStatusBarHidden: UIViewController? {
return self.topViewController
}
override var childForStatusBarStyle: UIViewController? {
return self.topViewController
}
}
I have two ViewControllers, using a custom segue from the first I present the second, with custom animator and a percent based interactor.
I want to slide up the status bar alongside the controller transition.
In the second ViewController I have this in order to hide the status bar
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .slide
}
override var prefersStatusBarHidden: Bool {
return true
}
I know I should call setNeedsStatusBarAppearanceUpdate() in an animation block in order to animate it, and use UIViewControllerTransitionCoordinator method animateAlongsideTransition: in order to make it follow the transition, but I am not sure where should I call this method.
I tried in the viewWillAppear method of the second controller but it still disappeared immediately without animation.
What is the right place to call this?
The reason why you couldn't see the animation is because in your second view controller you always return true to prefersStatusBarHidden, then such view controller starts with that condition, hence doesn't have any "chance" for playing the animation.
So in the second view controller you might try doing so:
class ViewController2: UIViewController {
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { get { return .slide } }
override var prefersStatusBarHidden: Bool { return statusBarHidden }
var statusBarHidden = false
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.statusBarHidden = true
UIView.animate(withDuration: 0.35) {
self.setNeedsStatusBarAppearanceUpdate()
}
}
}
Moreover in your Info.plist be sure to have View controller-based status bar appearance= YES
I followed this tutorial to smoothly hide the statusBar smoothly hide statusBar and everything works fine when I use it on practice projects. I use the code in other project's that do not have SplitVC but have a tabBar and uses a navVC & tableView and everything works fine. In those I can successfully make it appear/disappear.
In my actual project I'm using a SplitViewController for iPad. I noticed when I implemented the directions from the link to my SplitViewController the statusBar wouldn't hide. I then made a new project using Apple's default MasterDetailApp to make sure I wasn't doing anything wrong but it doesn't work there either. I kept all of Apple's original code and only added in the necessary methods to make the statusBar appear/disappear
in info.plist I added the View controller-based status bar appearance and set it to YES
in storyboard I added a purple button to the DetailVC to trigger the statusBar disappearance. I also added in the method to make the backBar button disappear/reappear
I added all the methods to make the statusBar disappear/disappear to the DetailVC scene.
I added a tapGesture to the scene to make the statusBar and backButton reappear
I clicked the Plus button on the Master scene, a date appeared, clicked it to get to the DetailVC, pressed the purple buttonPressed to hide the statusBar and backButton but only the backButton gets hidden. I touch the background and the backButton reappears. The statusBar doesn't move.
I kept all the original code from Apple's project's and added mines below it:
class DetailViewController: UIViewController {
//MARK:- Apple's code
#IBOutlet weak var detailDescriptionLabel: UILabel!
func configureView() {
if let detail = detailItem {
if let label = detailDescriptionLabel {
label.text = detail.description
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
configureView()
// make backButton and statusBar reappear when scene is tapped
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(showBackButtonAndStatusBar))
view.addGestureRecognizer(tapGesture)
}
var detailItem: NSDate? {
didSet {
configureView()
}
}
//MARK:- Outside of the tapGesture in viewDidLoad everything below here is what I added
// bool to determine wether to hide the statusBar or not
var statusBarShouldBeHidden = false
// api method to allow the staus bar to be hidden
override var prefersStatusBarHidden: Bool{
return statusBarShouldBeHidden
}
// api method to animate status bar appearance/disappearance
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation{
return .slide
}
#IBAction func buttonTapped(_ sender: UIButton) {
// 1. hide backBar button
navigationItem.setHidesBackButton(true, animated: false)
// 2. set bool to true
statusBarShouldBeHidden = true
UIView.animate(withDuration: 0.25){
// 3. api method to allow the statusBar to disappear
self.setNeedsStatusBarAppearanceUpdate()
}
}
//called when background is touched and added to tapGesture in viewDidLoad
#objc func showBackButtonAndStatusBar(){
// 1. set bool to false
statusBarShouldBeHidden = false
UIView.animate(withDuration: 0.25){
// 2. bring statusBar back
self.setNeedsStatusBarAppearanceUpdate()
}
// 3. bring backButton back
navigationItem.setHidesBackButton(false, animated: true)
}
}
How can I get the SplitViewVC to let me hide the statusBar?
It appears that you are trying to hide the status bar through the detail view controller. The status bar in the user interface is controlled only by the split view controller because it is on top of the view controller hierarchy. Therefore, the easiest way to control the behavior of the status bar is to subclass UISplitViewController and then override the prefersStatusBarHidden computed property in the subclass. Also, make sure you go to your storyboard and change the split view controller's custom class field in the Identity inspector to your subclass.
---Updated Answer---
#LanceSamaria Okay, I took your code above and tweaked some things. First of all, I only added the button action and not the tap gesture. Also, I commented out the hiding the back button, because this is important in the UI in order to be able to go back to the master view. Anyway, now when you click the button, the SplitViewController will hide the status bar. If you click the button again, then the status bar will reappear.
import UIKit
class DetailViewController: UIViewController {
#IBOutlet weak var detailDescriptionLabel: UILabel!
var statusBarShouldBeHidden = false
func configureView() {
// Update the user interface for the detail item.
if let detail = self.detailItem {
if let label = self.detailDescriptionLabel {
label.text = detail.description
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.configureView()
}
/* override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation{
return .slide
} */
var detailItem: NSDate? {
didSet {
// Update the view.
self.configureView()
}
}
#IBAction func buttonTapped(_ sender: UIButton) {
// 1. hide backBar button
//navigationItem.setHidesBackButton(true, animated: false)
// 2. set bool to true
statusBarShouldBeHidden = !statusBarShouldBeHidden
UIView.animate(withDuration: 0.25){
// 3. api method to allow the statusBar to disappear
guard let svc = self.splitViewController as? SplitViewController else { return }
svc.statusBarShouldBeHidden = self.statusBarShouldBeHidden
svc.setNeedsStatusBarAppearanceUpdate()
}
}
}
Also, one more really important thing. Below is my code for the split view controller subclass. Note that I use the same variable name "statusBarShouldBeHidden" in both the split view controller and the detail controller.
import UIKit
class SplitViewController: UISplitViewController {
var statusBarShouldBeHidden = false
override func viewDidLoad() {
super.viewDidLoad()
}
override var prefersStatusBarHidden: Bool {
return statusBarShouldBeHidden
}
}
Thank you for posting this question. This has helped my learn a lot trying to solve this problem. Please let me know if you still have a question about what this.
I have a view controller A that shows the status bar on top. From that view controller I want to present another view controller B that hides the status bar. In order to achieve that I override the property
override var prefersStatusBarHidden: Bool {
return true
}
on B. For enforcing a smooth animation whenever the status bar (dis)appears I also override the property
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .slide
}
However, when I now present view controller B from A the status bar disappears abruptly while A is still visible, right before the animated modal transition begins.
I'm searching for a way to fix this "jumping status bar" behavior. Ideally, I would like to have a clean separation:
A: shows status bar
B: does not show status bar
so that, when I present B, the status bar is covered by it.
As the status bar seems to be a global view that doesn't belong to any particular view controller it's probably difficult to achieve that kind of behavior. So in case it's not possible to replicate this exact animation behavior, I'd also be happy if the status bar slides out smoothly during the view controller transition. How can I achieve that?
For animating the status bar during the transition, you could do something like this in view controller B:
var willAppear = false
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .slide
}
override var prefersStatusBarHidden: Bool {
return willAppear
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
willAppear = true
UIView.animate(withDuration: 0.5) {
self.setNeedsStatusBarAppearanceUpdate()
}
}
Then I guess you would need to do the opposite if you want the reverse effect when the modal controller is dismissed.
You can adjust the duration of the animation to whatever suits you, though I'm not sure how consistent the duration between viewWillAppear and the modal controller actually being fully presented will be.
EDIT:
"Opposite" ends up being something like this (in view controller A):
var willAppear = false
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .slide
}
override var prefersStatusBarHidden: Bool {
return willAppear
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let _ = presentedViewController as? B {
willAppear = true
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
if let _ = presentedViewController as? B {
willAppear = false
UIView.animate(withDuration: 0.5) {
self.setNeedsStatusBarAppearanceUpdate()
}
}
}
Which I agree, is way too much code for something that I imagine many people want.