Present Uialertcontroller on Top of Everything Regardless of the View Hierarchy

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:

  1. Make your window the key and visible window (window.makeKeyAndVisible())
  2. Just use a plain UIViewController instance as the rootViewController of the new window. (window.rootViewController = UIViewController())
  3. 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



Leave a reply



Submit