Unable to present AlertController from a UIView in Swift - ios

I am trying to display an AlertController in Swift, but not from a UIViewController, but from a UIView that is called from its parent UIViewController. When I try to call the controller, I am getting the following error:
Warning: Attempt to present <UIAlertController: 0x7fa5544a3140> on
<UINavigationController: 0x7fa553830400> whose view is not in the window
hierarchy!
The code that I have which is trying to call the alertController is this:
let logInButton = TWTRLogInButton { (session, error) in
if let unwrappedSession = session {
let alert = UIAlertController(title: "Logged In",
message: "User \(unwrappedSession.userName) has logged in",
preferredStyle: UIAlertControllerStyle.Alert
)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
//self.presentViewController(alert, animated: true, completion: nil)
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alert, animated: true, completion: nil)
} else {
NSLog("Login error: %#", error!.localizedDescription);
}
}
The commented line in the above block of code is what the original block of code came with, and the line below the comment is the code I tried to replace it with. Can anyone see what it is I'm doing wrong?

Is the root view controller already presenting a view controller? If so, you may need to use:
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentedViewController?.presentViewController(alert, animated: true, completion: nil)
Having said that, it might be easier (and more consistent/appropriate) to use a delegate pattern and let the view tell whatever view controller is managing it to present the alert view controller.

In Swift 5
let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
window?.rootViewController?.presentedViewController?.present(alert, animated: true, completion: nil)

for swift 5 :
UIApplication.shared.keyWindow?.rootViewController?.presentedViewController?.present(alertName, animated: true, completion: nil)

Related

Use of unresolved identifier 'present' - the present method doesn't work anymore

I'm going to use UIAlertController but when I want to present it I see this error:
use of unresolved identifier 'present'
This is my code :
func showAllert(title: String, msg: String, vc: UIViewController){
let alert = UIAlertController(title: title, message: msg, preferredStyle: .alert)
let action = UIAlertAction(title: "ok", style: .default, handler: nil)
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
If you want to specifically use vc: UIViewController to present the alert you should call the method this way:
vc.present(alert, animated: true, completion: nil)
The problem is that your presenting view controller name is VC instead of present. If you do vc(alert, animated: true, completion: nil)
It will work.

Dismissing old UIAlertViewController before presenting new UIAlertViewController

I am new to swift, i want to dismiss the alert which is present on
screen when the new alert comes.
I tried:
func showDefaultAlert(controller: UIViewController, title: String, message: String) {
// create the alert
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
// add an action (button)
alert.addAction(UIAlertAction(title: defaultTextForNormalAlertButton, style: UIAlertActionStyle.Default, handler: nil))
// show the alert
//controller.presentViewController(alert, animated: true, completion: nil)
self.showAlert(controller, alert: alert)
}
func showAlert(controller: UIViewController, alert: UIAlertController) {
if let currentPresentedViewController = UIApplication.sharedApplication().keyWindow?.rootViewController?.presentedViewController {
if currentPresentedViewController.isKindOfClass(UIAlertController) {
currentPresentedViewController.dismissViewControllerAnimated(false, completion: {
controller.presentViewController(alert, animated: true, completion: nil)
})
}else {
controller.presentViewController(alert, animated: true, completion: nil)
}
}
}
}
// Call to above method in view controller class:
SPSwiftAlert.sharedObject.showDefaultAlert(self, title:"Title1", message1: "Message")
SPSwiftAlert.sharedObject.showDefaultAlert(self, title:"Title2", message: "Message2")
-
but the above code giving the run time error as :
Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<UIAlertController: 0x7fceb95dcfb0>)
After presenting the UIAlertController you can check its visibility as below:
Presented UIAlertController:
let alerts=UIAlertController(title: "Test", message: "Test", preferredStyle: .Alert)
presentViewController(alerts, animated: true, completion: nil)
Check if the UIAlertController is visible:
if (alerts.isViewLoaded())
{
print("Visible")
//Here you can dismiss the controller
//dismissViewControllerAnimated(true, completion: nil)
}
Check out this demo: Source code

Present a ViewController although UIAlertController is in the view Hierarchy

I want to presentViewController when an alertController is presented.
I want such as if user presses the Ok button now i want to show the ViewController from AppDelegate on PushNotification.
I came to know on SO that UIAlertController is presented on different thread so want to present the ViewController after user presses Ok Button and moves on to main thread as below:
func presentRScreenFromRootViewController()
{
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let rViewController = storyboard.instantiateViewControllerWithIdentifier("rsh") as! R_SVC
dispatch_async(dispatch_get_main_queue(),{
self.window?.rootViewController?.presentViewController(rViewController, animated: true, completion: nil)
});
}
But I get warning as:
Attempt to present <BB.R_SVC: 0x15b983600> on
<SWRevealViewController: 0x15652df00> which is already presenting
<UIAlertController: 0x15b4f2dd0>
This is because the viewcontroller is presenting alertcontroller.Other times its working well..My rootViewController changes and moves to next screen..But when there is UIAlertController i get warning as attempt to present viewcontroller which is not in the view hierarchy
What am i doing wrong?
when creating UIAlertController, place you VCpresenting code in the an UIAlertAction like so
let alert = UIAlertController(title: "your title", message "move to next VC?", preferredStyle: UIAlertController: UIAlertControllerStyle.Alert)
let action: UIAlertAction = UIAlertAction(title: "yes move to next VC", style: UIAlertActionStyle.Default) {action -> Void in
let rViewController = storyboard.instantiateViewControllerWithIdentifier("rsh") as! R_SVC
self.presentViewController(rViewController, animated: true, completion: nil) })
alert.addAction(defaultAction)
self.presentViewController(alert, animated: true, completion: nil)
edit: Maybe try Dismiss all UIAlertControllers currently presented.

UIAlertController Error When called in a modal presented view controller in swift

I have a view controller which is modally presented and when i run UIAlertController, keep getting a error message . how can i work around this.
the alert is trigged from a button which checks a text field is empty or not.
#IBAction func registerAction(sender: AnyObject) {
if(userEmail.isEmpty){
alertMessage("Fields Are Empty")
}
}
func alertMessage(userMessage:String){
var myAlert = UIAlertController(title: "Alert", message: userMessage, preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil)
myAlert.addAction(okAction)
self.presentViewController(myAlert, animated: true, completion: nil)
}
Error when i run the alert
Warning: Attempt to present <UIAlertController: 0x7f9bbb9060d0> on <AlertApp.CustomSignupVC: 0x7f9bb94accb0> which is already presenting <UIAlertController: 0x7f9bbb926660>
Try to add it in main operation queue.
dispatch_async(dispatch_get_main_queue()) {
self.presentViewController(myAlert, animated: true, completion: nil)
}
Check this relative question : Presenting a view controller modally from an action sheet's delegate in iOS8 - iOS10

Swift Attempt to present UIAlertController whose view is not in the window hierarchy (presented after TWTRShareEmailViewController)

I'm using the Twitter login in the signup process of my app. And I'm asking for the user's email. Once I get it, I'd like to present a UIAlertController.
Here's my code:
func askForTWMail(){
if (Twitter.sharedInstance().session() != nil) {
let shareMailVC=TWTRShareEmailViewController(completion: {(mail:String!, error:NSError!) in
if (mail != nil) {
print("GOT MAIL: \(mail)")
self.gotMail()
}else{
print("MAIL VC ERROR: \(error)")
}
})
println("PRESENT MAIL VC")
self.presentViewController(shareMailVC, animated: true, completion: nil)
}else{
println("User not logged in")
}
}
func gotMail(){
var alertController=UIAlertController(title: "Some title", message: "Some message", preferredStyle: UIAlertControllerStyle.Alert)
var okAction=UIAlertAction(title:"Yes", style: UIAlertActionStyle.Default) {
UIAlertAction in
//some action
}
var cancelAction=UIAlertAction(title:"No", style: UIAlertActionStyle.Cancel){
UIAlertAction in
//some action
}
alertController.addAction(okAction)
alertController.addAction(cancelAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
But I get this error (I guess because the TWTRShareEmailViewController is not dismissed):
Warning: Attempt to present UIALertController on xViewController whose
view is not in the window hierarchy!
Any idea of how I should write this? How can I know when the TWTRShareEmailViewController is dismissed to continue the signup process and be able to present my UIAlertController? I'm not aware of a delegate method related to TWTRShareEmailViewController.
Any help is appreciated. Thanks.
Found a solution here. I'm probably doing it wrong but if not it might be an Apple bug. The workaround is to delay the presentation of the UIAlertController:
dispatch_async(dispatch_get_main_queue(), ^{
self.presentViewController(alertController, animated: true, completion: nil)
})
EDIT: I found another workaround (I don't use the solution I put down here anymore). I had to change this because the Twitter login was also breaking my transitions between VCs.
I now call a specific UIViewController (I called it something like TWLoginVC) where I do all the Twitter login and other stuff. The view is just black so the user don't see the process is actually done in another VC (he just has to pick up the Twitter user he wants to login with). I guess you could also put a clear background to be even more invisible.
When I call this view controller and dismiss it, the transition is not applied to it and I don't have any more problem with it.
EDIT
Update for Swift:
DispatchQueue.main.async{
self.present(alertController, animated: true, completion: nil)
}
Here's an updated answer for Swift 3 tested on Xcode 8 based on Marie Dm's answer.
DispatchQueue.main.sync {
self.present(alertController, animated: true, completion: nil)
}
If DispatchQueue.main.sync caused crush, try DispatchQueue.main.async.
"async" worked for my problem while returning from contactPicker().
delay presenting viewController solved my problem and saved my day :D
Swift 4.0 :
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
//present your view controller her
self.presentViewController(alertController, animated: true, completion: nil)
}

Resources