3

I have a UIViewController (CameraViewController) and a UINavigationController (ListsNavController). They are embedded inside another view controller, MasterViewController, like this:

class MasterViewController {
    /// I also have `removeFromParent` and other cleanup, but I don't think it's relevant to my question...
    func embedCamera() { 
        self.addChild(cameraViewController)
        view.insertSubview(cameraViewController.view, at: 0)
            
        cameraViewController.view.frame = view.bounds
        cameraViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
            
        cameraViewController.view.layoutIfNeeded()
    }
    func embedLists() {
        self.addChild(listsNavController)
        view.addSubview(newViewController.view)
            
        listsNavController.view.frame = view.bounds
        listsNavController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
            
        listsNavController.view.layoutIfNeeded()
    }
}

This all works fine. However, I want to hide the status bar when CameraViewController is active, but show it when listsNavController is active. I'm doing it like this:

enum ViewControllerType {
    case camera
    case lists
}

class MasterViewController {
    var currentViewControllerType = ViewControllerType.camera
    override var prefersStatusBarHidden: Bool {
        if currentViewControllerType == .camera {
            return true
        } else {
            return false
        }
    }

    func goToLists() {
        listsNavController.view.frame.origin.x = view.frame.width
        UIView.animate(withDuration: 1, animations: {
            self.listsNavController.view.frame.origin.x -= self.view.frame.width
        }) { _ in

            /// on completion, I show the status bar
            self.currentViewControllerType = .lists
            self.setNeedsStatusBarAppearanceUpdate()
            
            self.listsNavController.didMove(toParent: self)
        }
    }
    func goToCamera() {
        /// I hide the status bar just before starting the animation
        self.currentViewControllerType = .camera
        self.setNeedsStatusBarAppearanceUpdate()
        
        listsNavController.view.frame.origin.x = 0
        UIView.animate(withDuration: 1, animations: {
            self.listsNavController.view.frame.origin.x += self.view.frame.width
        }) { _ in
            self.cameraViewController.didMove(toParent: self)
        }
    }
}

And this is what it looks like (going from cameraViewController to listsNavController and back):

going from "camera View Controller" to "lists Nav Controller" and back

The problem is that, when I show the status bar, the navigation bar and everything underneath it jumps down. (To keep the gif small I didn't show what's underneath the bar, but everything jumps.) Also, when I come back (from listsNavController to cameraViewController), the navigation bar's title animates, but everything underneath still jumps.

Is there a way to animate that jump so that it's smooth? Or preferably, have no jump at all (Have some sort of "placeholder" above the navigation bar when the status bar is hidden")?

Thanks in advance. I can provide more code if needed.

aheze
  • 24,434
  • 8
  • 68
  • 125
  • 1
    Wrap your call to `setNeedsStatusBarAppearanceUpdate()` inside an animation block along with a call to `layoutIfNeeded()` and it will be animated. – matt Oct 03 '20 at 20:10
  • @matt Thanks, that gets rid of the jump! You can post it as an answer and I'll accept it. But if there was a way to have some sort of placeholder that would be great – aheze Oct 03 '20 at 20:19
  • 1
    Things move when the status bar disappears because those things are pinned to the top of the safe area. If you don't want things to move then don't pin them to the top of the safe area. – matt Oct 03 '20 at 20:28

0 Answers0