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?
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)
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