Search code examples
ioskeyboarduiwindow

How to overlay a view above a keyboard in iOS


I need to present a help screen that overlays an open keyboard - the help screen should dim the whole view underneath and keep just a small hole with full transparency to "highlight" that piece. The point is to provide some information about several view components while highlighting them. Without a keyboard, I could just put a view at top of the hierarchy, but in this case the UI uses a keyboard with a custom input accessory that needs to be visible.

I tried to insert a new UIWindow and put it above all the UIWindows:

class CustomTextField: UITextField {
    override var canResignFirstResponder: Bool {
        return false
    }
}

class ViewController: UIViewController {
    var textField: UITextField = CustomTextField()

    override func viewDidAppear(_ animated: Bool) {
        view.backgroundColor = .white
        super.viewDidAppear(animated)

        textField.frame = CGRect(x: 0, y: 0, width: 200, height: 50)
        view.addSubview(textField)
        textField.backgroundColor = UIColor.gray
        textField.becomeFirstResponder()

        DispatchQueue.main.asyncAfter(wallDeadline: .now() + 1) {

            self.window.windowLevel = 100000002.0 // based on experiments with UIApplication.shared.windows this should be the top most window
            let controller = UIViewController()
            controller.view.backgroundColor = UIColor.black.withAlphaComponent(0.5)
            self.window.rootViewController = controller
            self.window.makeKeyAndVisible()
        }
    }
    let window = UIWindow(frame: UIScreen.main.bounds)
}

But there are two problems with this approach:

  1. The keyboard gets hidden as soon as the window becomes key and visible.
  2. Even when using windowLevel = 100000002.0 it seems that the keyboard is above the window (the keyboard gets animated, so while hiding, I can see that its above my window).

Any ideas how to deal with these two problems? Is it even possible?


Solution

  • OK, as pointed out by @Krunal, this is kind of a duplicate of this question. The trick there is to add the overlay view to the window in which keyboard is (which happens to be the UIApplication.shared.windows.last):

    class ViewController: UIViewController {
        var textField: UITextField = UITextField()
    
        override func viewDidAppear(_ animated: Bool) {
            view.backgroundColor = .white
            super.viewDidAppear(animated)
    
            textField.frame = CGRect(x: 0, y: 0, width: 200, height: 50)
            view.addSubview(textField)
            textField.backgroundColor = UIColor.gray
            textField.becomeFirstResponder()
    
            DispatchQueue.main.asyncAfter(wallDeadline: .now() + 1) {
                // this does the trick
                let customView = UIView(frame: self.view.bounds)
                customView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
                customView.layer.zPosition = CGFloat(Float.greatestFiniteMagnitude)
                UIApplication.shared.windows.last?.addSubview(customView)
            }
        }
    }