Search code examples
iosswiftxcodexibaddsubview

How to keep a UIView always showing over multiple views


I wish to create and present a view over each view controller in my app allowing users to interact with it or the view controllers beneath it. I want the view to always appear on top of whatever view I may present or segue to.

I have a custom UIView that appears when users tap a table view cell. Within the didSelectRowAt tableView function I have tried:

(UIApplication.shared.delegate as! AppDelegate).window?.addSubview(self.subView!)

and

self.view.addSubview(self.subView!)

Both function similarly with the view appearing over the current View controller and allowing users to interact with the table still. But when I present a new ViewController the subView disappears as it has not been added to the new View.


Solution

  • Subclass UIWindow, and override addSubview() to make sure your overlay view is always on top:

    weak var overlay: MyOverlayView!
    
    override func addSubview(_ view: UIView) {
        if let overlay = view as? MyOverlayView {
            self.overlay = overlay 
            super.addSubview(overlay)
        } else {
            self.insertSubview(view, belowSubview: overlay)
        }
    }
    

    You must make sure the app delegate uses your custom class as the app's main window, and not the superclass UIWindow. For this, remove the "Main storyboard file base name" entry from your Info.plist and instead instantiate the main window manually in your AppDelegate:

    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
    
        var window: MyCustomWindow?
    
    
        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
            self.window = MyCustomWindow(frame: UIScreen.main.bounds)
    
            // 1. Add your overlay view to the window
            // (...)
    
            // 2. Load your initial view controller from storyboard, or 
            //  instantiate it programmatically
            // (...)
    
            window?.makeKeyAndVisible()
            return true
        }
    

    Note: I haven't tested this code. Let me know in the comments if there's a problem.