[Presentation] Attempt to present UIAlertController on OverFullScreenModalViewController (from OverFullScreenModalViewController) whose view is not in the window hierarchy

Hi,

I have a ViewController that presents another ViewController as a modal over full screen when a button is touched.

let vc = UIStoryboard(name: "***", bundle: nil).instantiateViewController(withIdentifier: "OverFullScreenModalViewController") as! OverFullScreenModalViewController
vc.modalPresentationStyle = .overFullScreen
vc.modalTransitionStyle = .crossDissolve

self.present(vc, animated: true, completion: nil)

Then, on my modal ViewController I have a button that, when touched, I want to present a confirmation alert

@IBAction func handleDelete(_ sender: Any) {
        let negativeHandler:((Any) -> ()) = {(_) in
            //do nothing
        }

        let handler:((Any) -> ()) = {(_) in
            // Delete
        }

        DispatchQueue.main.async {
            let alert = UIAlertController(title: "Please confirm", message: "Are you sure?", preferredStyle: .alert)
            let positiveAction = UIAlertAction(title: "yes", style: .default, handler: handler)
            alert.addAction(UIAlertAction(title: "no", style: .cancel, handler: negativeHandler))
            alert.addAction(positiveAction)
            alert.preferredAction = positiveAction

            self.present(alert, animated: true)
        }
}

But what happens is that the modal is dismissed and I get this error on the console:

[Presentation] Attempt to present <UIAlertController: 0x7ff78d37cc00> on <OverFullScreenModalViewController: 0x7ff792814200> (from <OverFullScreenModalViewController: 0x7ff792814200>) whose view is not in the window hierarchy.

I've tried with and without DispatchQueue.main.async, nothing is trying to be presented on viewDidLoad, viewWillAppear or viewDidAppear

Any ideas?

Accepted Reply

You should make sure that alert is presented from its view hierarchy.

So, find the topMost VC in its hierarchy

    func getTopMostViewController() -> UIViewController? {
        var topMostViewController = UIApplication.shared.keyWindow?.rootViewController

        while let presentedViewController = topMostViewController?.presentedViewController {
            topMostViewController = presentedViewController
        }

        return topMostViewController
    }

And present from it:

getTopMostViewController()?.present(alert, animated: true, completion: nil)

Credit: https://stackoverflow.com/questions/54209766/swift-4-attempt-to-present-viewcontroller-whose-view-is-not-in-the-window-hierar

Note: as keyWindow is now deprecated, you may have to replace

var topMostViewController = UIApplication.shared.keyWindow?.rootViewController

by:

var topMostViewController = UIApplication.shared.windows[0].rootViewController

or

let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first as? UIWindowScene
guard let window = windowScene?.windows.first else { return nil }
var topMostViewController = window.rootViewController
  • Hi! Thanks for the response! I did that but the ViewController I'm presenting over the full screen (OverFullScreenModalViewController) dismisses before presenting the UIAlertController =/ which is not the desired behavior

  • Oh no, my bad, I figured out why it was dismissing. Yep! this worked out perfectly! Thanks!

Add a Comment

Replies

You should make sure that alert is presented from its view hierarchy.

So, find the topMost VC in its hierarchy

    func getTopMostViewController() -> UIViewController? {
        var topMostViewController = UIApplication.shared.keyWindow?.rootViewController

        while let presentedViewController = topMostViewController?.presentedViewController {
            topMostViewController = presentedViewController
        }

        return topMostViewController
    }

And present from it:

getTopMostViewController()?.present(alert, animated: true, completion: nil)

Credit: https://stackoverflow.com/questions/54209766/swift-4-attempt-to-present-viewcontroller-whose-view-is-not-in-the-window-hierar

Note: as keyWindow is now deprecated, you may have to replace

var topMostViewController = UIApplication.shared.keyWindow?.rootViewController

by:

var topMostViewController = UIApplication.shared.windows[0].rootViewController

or

let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first as? UIWindowScene
guard let window = windowScene?.windows.first else { return nil }
var topMostViewController = window.rootViewController
  • Hi! Thanks for the response! I did that but the ViewController I'm presenting over the full screen (OverFullScreenModalViewController) dismisses before presenting the UIAlertController =/ which is not the desired behavior

  • Oh no, my bad, I figured out why it was dismissing. Yep! this worked out perfectly! Thanks!

Add a Comment