2

Different vcs inside my app show the status bar visible and others are hidden. This is set to YES in the info.pList

 "View controller-based status bar appearance": YES

 // also tried togging this between yes and no
 "Status bar is initially hidden": YES

The app has 2 windows, the main window and a second window. The second window gets presented it front of the main window on a button push. The vc in the second window has the status bar hidden.

The problem is if I'm on a vc (mainVC) inside the main window that shows the status bar, I press the button to show the second window, mainVC's status bar disappears. The second window gets presented and after I dismiss it I send a notification to mainVC to call setNeedsStatusBarAppearanceUpdate() but prefersStatusBarHidden isn't triggered so the status bar stays hidden even though it shouldn't be. I even subclassed a Navigation Controller and added the code there with mainVC as it's root.

Why isn't prefersStatusBarHidden getting called?

I added prefersStatusBarHidden inside the mainVC by itself, the nav to the mainVC by itself, and then in both the mainVC and it's nav at the same time. It's still not getting called after setNeedsStatusBarAppearanceUpdate() gets called in either places.

Subclassed nav:

class MainVCNavController: UINavigationController {

    override init(rootViewController: UIViewController) {
        super.init(rootViewController: rootViewController)

        NotificationCenter.default.addObserver(self, selector: #selector(updateStatusBar), name: NSNotification.Name(rawValue: "updateStatusBar"), object: nil)
    }

    let statusBarHidden: Bool = false

    @objc func updateStatusBar() {
        self.setNeedsStatusBarAppearanceUpdate() // this gets called when the notification is triggered
    }

    override var prefersStatusBarHidden: Bool {
        return statusBarHidden // this doesn't get called after setNeedsStatusBarAppearanceUpdate() is called 
    }

    // I added this just to see if it would make a difference but it didn't
    override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
        return .slide
    }

    override open var childViewControllerForStatusBarStyle: UIViewController? {
        return self.topViewController
    }

    override open var childViewControllerForStatusBarHidden: UIViewController? {
        return self.topViewController
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }
}

MainVC is the rootVC of the above nav

class MainVCController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(updateStatusBar), name: NSNotification.Name(rawValue: "updateStatusBar"), object: nil)
    }

    let statusBarHidden: Bool = false

    @objc func updateStatusBar() {
        self.setNeedsStatusBarAppearanceUpdate() // this gets called when the notification is called
    }

    override var prefersStatusBarHidden: Bool {
        return statusBarHidden // this doesn't get called after setNeedsStatusBarAppearanceUpdate() is triggered 
    }

    // I added this just to see if it would make a difference but it didn't
    override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
        return .slide
    }
}

SecondVC inside the second window has it's status bar hidden. It sends the notification when it's dismissed to the above mainVC:

class SecondController: UIViewController {

    override var prefersStatusBarHidden: Bool {
        return true
    }

    if dismissed {
       NotificationCenter.default.post(name: Notification.Name(rawValue: "updateStatusBar"), object: nil)
    }
}

I also read that I need to call the below to trigger the prefersStatusBarHidden but even when I added these to the updateStatusBar() it didn't make a difference.

navigationController?.setNavigationBarHidden(false, animated: false)
// or
navigationController?.navigationBar.isHidden = false
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
  • You need to write `prefersStatusBarHidden` in the visible controller i.e. in the second controller to take action – zombie Jun 30 '18 at 19:17
  • Do you mean the second window? The vc inside the second window, let's call it secondVC does have prefersStatusBarHidden set to true on it. It doesn't show the status bar. I'll update the question to show it – Lance Samaria Jun 30 '18 at 19:20
  • 1
    to understand what I mean check this [answer](https://stackoverflow.com/a/19365160/6689101) – zombie Jun 30 '18 at 19:27
  • @zombie I actually read that this morning, thanks. After I read that I took it as the nav determines that status bar since it manages everything. That's when I subclassed the navVC in the question and added the prefersStatusBarHidden there but it didn't make a difference – Lance Samaria Jun 30 '18 at 19:30
  • I added prefersStatusBarHidden inside the mainVC by itself, the nav to the mainVC by itself, and then in both the mainVC and it's nav at the same time. It's still not getting called. It only gets called when I switch tabs and back – Lance Samaria Jun 30 '18 at 19:31
  • There are two cases either your application is trying to get the hidden state from another controller so try a breakpoint or the notification is called from the background so try dispatchQueue.main.async {} on setting needs update – zombie Jun 30 '18 at 19:49
  • thanks for the advice. I'll try putting setNeedsStatusBarAppearanceUpdate() on the main queue, I never thought about that. My app is tabBar based, if it's getting the hidden state from another controller seems then only way to find out is to put a break point on every prefersStatusBarHidden that it's inside a vc. I'll try them both and get back to you in a few. Thanks again – Lance Samaria Jun 30 '18 at 19:53
  • 1
    no use [Symbolic Breakpoints](https://www.bugsee.com/blog/advanced-guide-using-breakpoints-xcode/) – zombie Jun 30 '18 at 19:58
  • @zombie do you ind making this an official answer. You was correct about putting setNeedsStatusBarAppearanceUpdate() on the main queue. i also added [weak self] just to be safe but either way it works. Btw you should add what you wrote about both cases and the link to the symbolic breakpoints. Thank you very much I ill accept and upvote your answer. Enjoy your day! – Lance Samaria Jun 30 '18 at 21:02

2 Answers2

2

Updating the status bar needs to be on the main thread.

There are two ways to ensure that:

Add notification observer on the main thread: (you don't need to expose the func to objc c):

NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "updateStatusBar"), object: nil, queue: .main, using: updateStatusBar)

func updateStatusBar(_ notification: Notification) {
    setNeedsStatusBarAppearanceUpdate()
}

Or update the status bar on the main thread:

NotificationCenter.default.addObserver(self, selector: #selector(updateStatusBar(_:)), name: NSNotification.Name(rawValue: "updateStatusBar"), object: nil)

@objc func updateStatusBar(_ notification: Notification) {

    DispatchQueue.main.sync {
        self.setNeedsStatusBarAppearanceUpdate()
    }
}
zombie
  • 5,069
  • 3
  • 25
  • 54
  • @LanceSamaria by the way if you remove the observer in `deinit` then you don't need the `weak self` – zombie Jul 01 '18 at 10:49
1

@zombie’s answer about the updating on the main thread 100% worked. On another note he also suggested I use symbolic breakpoints to diagnose the problem. He provided a great link to help:

Symbolic Breakpoints

Lance Samaria
  • 17,576
  • 18
  • 108
  • 256