Search code examples
iosswiftuiviewuiwindow

How do I add a view to the top of an app in iOS 13/14


I'm trying to add a view to my app that is persistent across flows and sits above all other views. In previous versions of iOS i've been able to simply add a subview to the UIWindow that sat atop my application however it would appear that as of iOS 13 this is no longer possible.

I've tried the following, but to no success: UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.addSubview(someView)

Debugging a bit further, it appears that my app does not have a window at all present on its view hierarchy. I know this because when putting a breakpoint on the follow forEach statement, I never get a breakpoint to execute.

UIApplication.shared.windows.forEach { (window) in
    window.addSubview(myView)
}

Any advice on how to achieve this, on all iOS versions 11 through 14 would be much appreciated.


Solution

  • You have to create a second UIWindow, assign the rootViewController and make it visible.

    The code bellow is for SwiftUI, but you can do the same with UIKit, just create a window and set window.isHidden = false.

    let secondWindow = UIWindow(windowScene: windowScene)
    secondWindow.frame = CGRect(x: 0, y: 40, width: UIScreen.main.bounds.size.width, height: 100)
    let someView = Text("I am on top of everything")
    secondWindow.rootViewController = UIHostingController(rootView: someView)
    secondWindow.isHidden = false
    

    Depending on how many windows you have. You might need to change the windowLevel of your second window. You can check if the window is displayed using Debug View Hierarchy from Xcode.

    This is a UIKit example without SceneDelegate. If you have scene delegate you have to pass the window scene to the UIWindow init. Don't forget to retain the second window.

    
    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
        private enum Constants {
            static let sessionConfiguration = URLSessionConfiguration.default
        }
    
        var window: UIWindow?
        var secondWindow: UIWindow?
    
        // MARK: - UIApplicationDelegate
    
        func application(_ application: UIApplication,
                         didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
    
            let window = UIWindow(frame: UIScreen.main.bounds)
            self.window = window
            let viewController = UIViewController()
            viewController.view.backgroundColor = .red
    
            window.rootViewController = viewController
            window.makeKeyAndVisible()
    
            let secondWindow = UIWindow()
            secondWindow.frame = CGRect(
                x: 0,
                y: 40,
                width: UIScreen.main.bounds.size.width,
                height: 100
            )
    
            let secondController = UIViewController()
            secondController.view.backgroundColor = .blue
            secondWindow.rootViewController = secondController
            secondWindow.isHidden = false
            self.secondWindow = secondWindow
    
            return true
        }
    }
    

    enter image description here