Search code examples
iosswiftxcode13

How to create a header layer that would stay the same over several ViewControllers?


I'd like to create a header that wouldn't move at all in my application that contains several ViewControllers. My header is simply a view containing a logo (UIImageView), a UIButton and a UILabel. I've heard that I can do that with a UINavigationController or by adding a subview to the UIWindow, but it doesn't work for me (maybe I'm doing it wrong). My main issue is to keep it fixed at the top of the screen (like a hud?) during segue animations, and I would also need to be able to modify the content of the UILabel during the application process.

I work on Xcode 13 with a storyboard for iOS 15 on iPad. Hope I've been clear enough.

EDIT It appears that inserting a view in the UIWindow would be the best solution for me, though I'm not sure how to do that properly. All the examples I find don't work and are deprecated/outdated.


Solution

  • So finally I succeeded using the method of Imran to which I added few things to match what I needed.

    I have a singleton class for my header ViewController that contains the elements I needed inside of it.

    let HEADER = STORYBOARD.instantiateViewController(withIdentifier: "HeaderLayer") as? HeaderViewController
    
    class HeaderViewController: UIViewController {
    
        @IBOutlet weak var ThemeLabel: UILabel!
        @IBOutlet weak var DifficultyImage: UIImageView!
        @IBOutlet weak var ExitButton: UIButton!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // ...
        }
    
        // Methods
    }
    

    And here's how I put my header in the UIWindow to make it appear at the top of the screen, where it will stay fixed even during segue animations. I change the frame because in full screen it would disable the interactions for the views underneath.

    let STORYBOARD = UIStoryboard(name: "Main", bundle: nil)
    
    class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    
        var window: UIWindow?
    
        func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        
            HEADER!.view.frame = CGRect(x: 0, y: 0, width: 1366, height: 142)
            window?.addSubview(HEADER!.view)
            window?.makeKeyAndVisible()
        
            // ...
        }
        // ...
    }
    

    Then it's necessary to bring the header to front everytime one of my ViewControllers appears.

    class ViewController: UIViewController {
    
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
        
            let window = UIApplication.shared.keyWindow
            window?.bringSubviewToFront(HEADER!.view)
        
            // ...
        }
    }
    

    I'm not using any UINavigationController or UITabBarController, but it is possible with this method.

    The only problem I encountered is that UIApplication.shared.keywindow is deprecated. But it doesn't seem to cause any issue and I didn't find a solution yet.