Present UIAlertController on top of everything regardless of the view hierarchy
Update Dec 16, 2019:
Just present the view controller/alert from the current top-most view controller. That will work :)
if #available(iOS 13.0, *) {
if var topController = UIApplication.shared.keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
topController.present(self, animated: true, completion: nil)
}
Update July 23, 2019:
IMPORTANT
Apparently the method below this technique stopped working in iOS 13.0 :(
I'll update once I find the time to investigate...
Old technique:
Here's a Swift (5) extension for it:
public extension UIAlertController {
func show() {
let win = UIWindow(frame: UIScreen.main.bounds)
let vc = UIViewController()
vc.view.backgroundColor = .clear
win.rootViewController = vc
win.windowLevel = UIWindow.Level.alert + 1 // Swift 3-4: UIWindowLevelAlert + 1
win.makeKeyAndVisible()
vc.present(self, animated: true, completion: nil)
}
}
Just setup your UIAlertController, and then call:
alert.show()
No more bound by the View Controllers hierarchy!
Present an UIAlertController on top of everything and stay on top even if another view is pushed (iOS 13)
You can use the same implementation as you used in iOS 12 and below, but you must hold a strong reference to the window you are presenting the alert on.
In your current code - when running on iOS 13 - the alertWindow
will be destroyed as soon as showHighPriorityNotification
finishes, dismissing your alert. It can be fixed by holding a strong reference to the alertWindow
somewhere else.
Check this answer for a way of implementing that.
How to present UIAlertController when not in a view controller?
I posted a similar question a couple months ago and think I've finally solved the problem. Follow the link at the bottom of my post if you just want to see the code.
The solution is to use an additional UIWindow.
When you want to display your UIAlertController:
- Make your window the key and visible window (
window.makeKeyAndVisible()
) - Just use a plain UIViewController instance as the rootViewController of the new window. (
window.rootViewController = UIViewController()
) - Present your UIAlertController on your window's rootViewController
A couple things to note:
- Your UIWindow must be strongly referenced. If it's not strongly referenced it will never appear (because it is released). I recommend using a property, but I've also had success with an associated object.
- To ensure that the window appears above everything else (including system UIAlertControllers), I set the windowLevel. (
window.windowLevel = UIWindowLevelAlert + 1
)
Lastly, I have a completed implementation if you just want to look at that.
https://github.com/dbettermann/DBAlertController
Present ViewController on top of everything regardless of the view hierarchy in IOS 13
I found the solution:
if #available(iOS 13.0, *) {
if var topController = UIApplication.shared.keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
self.modalPresentationStyle = .fullScreen
topController.present(self, animated: true, completion: nil)
}
Presenting Alert Controller from the delegate
Use below line:
self.window?.rootViewController?.present(alertController, animated: true, completion: nil)
Instead of
vc.present(alertController, animated: true, completion: nil)
than problem is solved.
Example:
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5, execute: {
let alertController = UIAlertController(title: "Alert Title", message:
"Message", preferredStyle: .actionSheet)
let okAction = UIAlertAction(title: "Ok", style:.default) {
UIAlertAction in
NSLog("OK Pressed")
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) {
UIAlertAction in
NSLog("Cancel Pressed")
}
alertController.addAction(okAction)
alertController.addAction(cancelAction)
self.window?.rootViewController?.present(alertController, animated: true, completion: nil)
})
How to hide the status bar when presenting a UIAlertController in a non-UIViewController class?
Hope this help you.
To hide status bar call this method(in your case before presenting alertview controller)
func hideStatusBar() {
UIApplication.shared.keyWindow?.windowLevel = UIWindowLevelStatusBar
}
And to get back status bar call this method(after dismissing alertview controller)
func updateStatusBarToPreviousState() {
UIApplication.shared.keyWindow?.windowLevel = UIWindowLevelNormal
}
UIViewController Double Modal Presentation
You can't present two modals from one UIViewController
instance at the time.
If you would like to present UIAlertController of top of your modal you can use new UIWindow
.
For details take a look at: How to present UIAlertController when not in a view controller?
or
ios - present UIAlertController on top of everything regardless of the view hierarchy
UIAlertController won't display - In Swift
The problem is that by the time you call this following function:
present(alertController, animated: true, completion: nil)
your mail view controller is still visible. You have to make sure that the alertController
is presented on the top of window hierarchy. In your example, you have the following window hierarchy:
+---------------------------+
| MailComposeViewController |
+---------------------------+
||
+---------------------------+
| ContactUsViewController |
+---------------------------+
What you should do instead is to dismiss the email view controller first. When that is done, show you alert view controller.
let alertController = UIAlertController(title: "test", message: "test", preferredStyle: .Alert)
let okButton = UIAlertAction(title: "Okay", style: .Default, handler: nil)
alertController.addAction(okButton)
controller.dismissViewControllerAnimated(true){ () -> Void in
self.present(alertController, animated: true, completion: nil)
}
Alternatively, you can also present your alertController
on top of the MailComposeViewController
, like so:
let alertController = UIAlertController(title: "test", message: "test", preferredStyle: .Alert)
let okButton = UIAlertAction(title: "Okay", style: .Default, handler: nil)
alertController.addAction(okButton)
controller.present(alertController, animated: true, completion: nil)
ios 10+, Swift 3+ - Cannot dismiss UIAlertController from Singleton instance
In order to dismiss the UIAlertController
(which is a subclass of UIViewController
), it should be sufficient to call the dismiss
method:
func hide() {
dismiss(animated: true, completion: nil)
}
It works fine in my sample project.
Related Topics
Generics in Swift - "Generic Parameter 'T' Could Not Be Inferred
Uitableview:Viewforheaderinsection: Not Called During Reloaddata:
How to Show Uipickerview When Selecting Uitextfield
Custom Font in iOS Not Working
Why the Extension Is "Momd" But Not "Xcdatamodel" When Search the Path for the Model File
iOS Facebook Sdk 4.0 Login Error Code 304
Get Camera Field of View in iOS 11 Arkit
Cropping Center Square of Uiimage
Where Does the Indexpath of Dequeuereusablecellwithidentifier:Forindexpath: Get Used
How to Get the Status Bar Height in iOS 13
Xcode 8 Recommend Me to Change the Min iOS Deployment Target from 7.1 to 8.0
How to Import Private Framework Headers in a Swift Framework
Should I Be Using Awakefromnib or Initwithcoder Here
How to Add Done Button on Keyboard on Top of Keyboard in iOS
How to Programmatically Check Support of 'Face Id' and 'Touch Id'