Search code examples
iosiphoneswiftswift3uistatusbar

Navbar not animating while hiding but still animates while reappearing


I followed this answer and added a UIGestureRecognizer to show or hide the Navbar and Toolbar. Strangely, the Navbar is not silding while hiding but it still slides when it reappears. the toolbar is animating all the time.

I changed the code to:

override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
    print("got called")
    return UIStatusBarAnimation.slide
}

to see if it's being called when it hides and it is getting called. I also tried setting automaticallyAdjustsScrollViewInsets to false as suggested by the comments from the same answer but still no luck.

I can't find any information about this issue anywhere.

EDIT: here's my complete code:

override func viewDidLoad(){
    super.viewDidLoad()
    self.automaticallyAdjustsScrollViewInsets = false
    let gesture = UITapGestureRecognizer(target: self, action: #selector(toggle))
    view.isUserInteractionEnabled = true
    view.addGestureRecognizer(gesture)
}

func toggle() {
    navigationController?.setNavigationBarHidden(navigationController?.isNavigationBarHidden == false, animated: true)
    navigationController?.setToolbarHidden(navigationController?.isToolbarHidden == false, animated: true)
}
override var prefersStatusBarHidden: Bool {
    print("got called 1")
    return navigationController?.isNavigationBarHidden == true
}

override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
    print("got called 2")
    return UIStatusBarAnimation.slide
}

Solution

  • The following Swift 4 code works fine with iOS 11 but does not work as expected for iOS 10. When using it with iOS 10, for some reason, the navigation bar does not animate when hiding and suddenly disappears.

    import UIKit
    
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let gesture = UITapGestureRecognizer(target: self, action: #selector(toggle))
            view.addGestureRecognizer(gesture)
        }
    
        @objc func toggle() {
            navigationController?.setNavigationBarHidden(navigationController?.isNavigationBarHidden == false, animated: true)
        }
    
        override var prefersStatusBarHidden: Bool {
            return navigationController?.isNavigationBarHidden == true
        }
    
        override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
            return UIStatusBarAnimation.slide
        }
    
    }
    

    In order to fix this, you can use the following ViewController implementation that uses UIViewController's setNeedsStatusBarAppearanceUpdate() property:

    class ViewController: UIViewController {
    
        override func viewDidLoad(){
            super.viewDidLoad()
    
            let gesture = UITapGestureRecognizer(target: self, action: #selector(toggle))
            view.addGestureRecognizer(gesture)
        }
    
        private var isHidden = false
    
        @objc func toggle() {
           navigationController!.setNavigationBarHidden(!isHidden, animated: true)
           isHidden = navigationController!.isNavigationBarHidden
    
            UIView.animate(withDuration: 0.3) {
                self.setNeedsStatusBarAppearanceUpdate()
            }
        }
    
        override var prefersStatusBarHidden: Bool {
            return isHidden
        }
    
        override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
            return UIStatusBarAnimation.slide
        }
    
    }
    

    As an alternative, you can also use the UIViewController implementation below:

    import UIKit
    
    class ViewController: UIViewController {
    
        override func viewDidLoad(){
            super.viewDidLoad()
    
            let gesture = UITapGestureRecognizer(target: self, action: #selector(toggle))
            view.addGestureRecognizer(gesture)
        }
    
        private var isHidden: Bool = false {
            willSet {
                self.navigationController!.setNavigationBarHidden(!self.isHidden, animated: true)
            }
            didSet {
                UIView.animate(withDuration: 0.3) {
                    self.setNeedsStatusBarAppearanceUpdate()
                }
            }
        }
    
        @objc func toggle() {
            isHidden = !isHidden
        }
    
        override var prefersStatusBarHidden: Bool {
            return isHidden
        }
    
        override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
            return UIStatusBarAnimation.slide
        }
    
    }
    

    See this answer that shows up to 3 different ways to toggle both status bar and navigation bar for Swift 4 and iOS 11.