UIAlertController won't display - In Swift
Asked Answered
W

4

12

I have created a ContactUsViewController.

In this controller the user will select a option from a pickerView and then type a message in a textView and press the Send Email button.

When they press the button, it creates a MFMailComposeViewController so they can send the email. Now, when the email is either Sent, Saved, Cancelled or Failed, the MFMailComposeViewController closes as they are back in my app. I want an alert to then appear to give them an update, on what ever just happened. I have originally set this up using a UIAlertView, and have placed this code in the fun mailComposeController function, see below:

func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {

    switch result.rawValue {

    case MFMailComposeResultCancelled.rawValue:
        NSLog("Email cancelled")
    case MFMailComposeResultSaved.rawValue:
        NSLog("Email saved")
        let sendMailErrorAlert = UIAlertView(title: "Email saved", message: "Your email has been saved in Mail.", delegate: self, cancelButtonTitle: "OK")
        sendMailErrorAlert.show()
    case MFMailComposeResultSent.rawValue:
        NSLog("Email sent")

        let alertController = UIAlertController(title: "test", message: "test", preferredStyle: .Alert)
        let okButton = UIAlertAction(title: "Okay", style: .Default, handler: nil)
        alertController.addAction(okButton)
        presentViewController(alertController, animated: true, completion: nil)

    case MFMailComposeResultFailed.rawValue:
        NSLog("Email failed: %@", [error!.localizedDescription])
        let sendMailErrorAlert = UIAlertView(title: "Oops!", message: "Looks like something went wrong, and the email couldn't send. Please try again later.", delegate: self, cancelButtonTitle: "OK")
        sendMailErrorAlert.show()
    default:
        break

    }

As you can see, I have used the UIAlertView for Email Saved and Email failed. This works absolutely fine, and shows my alert as expected.

I recently read that UIAlertView is depreciated since iOS 8, and that we should now use UIAlertController. Therefore I tried creating the same thing using the UIAlertController, which you can see for the Email Sent section. However, this doesn't seem to work, it just doesn't show any alert. It does print this error into the logs:

Warning: Attempt to present <UIAlertController: 0x126c50d30> on <XXXX.ContactUsViewController: 0x1269cda70> whose view is not in the window hierarchy! 

But I'm not really sure what this is saying, or more importantly, how to fix it.

My questions are:

  1. Am I right in saying I cannot use UIAlertView?
  2. How can I fix this and make the UIAlerController appear after I have returned from the Mail app?

Thanks in advance.

Wristlet answered 6/3, 2016 at 15:36 Comment(0)
M
12

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)
Minium answered 6/3, 2016 at 16:16 Comment(0)
P
4

I was facing the same problem now after presenting an alert in DispachQueue.main.async{}, code is working fine.

Palstave answered 19/9, 2020 at 12:57 Comment(0)
B
3

I needed an actionSheet rather than an alert, and due to my specific circumstances, none of the solutions here worked. If your UIAlertController isn't showing, an alternative way to do this is to always put it as the topmost window (in Swift 4):

func topmostController() -> UIViewController? {
    if var topController = UIApplication.shared.keyWindow?.rootViewController {
        while let presentedViewController = topController.presentedViewController {
            topController = presentedViewController
        }
        return topController
    }
    return nil
}

// Now put up the menu
let menu=UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
menu.addAction(UIAlertAction(title: "Test", style: .default, handler: {_ in self.doTest()} ))
menu.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: {_ in self.doCancel()} ))
topmostController()?.present(menu, animated: true, completion: nil)

This displays the window regardless of your view controller stack.

Band answered 6/12, 2018 at 12:0 Comment(1)
This solution worked in my situation where I have two viewcontroller and uiview on top of it. Thanks.Tutu
A
-1

Swift 4

Do Not Show Again Alert message

In DidLoad

let AlertOnce = UserDefaults.standard
    if(!AlertOnce.bool(forKey: "oneTimeAlert")){


        let messageTitle = "Your selected branch is \(storeIDName)"
        let alertView = UIAlertController(title: messageTitle, message: "", preferredStyle: .actionSheet)
        let DoNotShowAgainAction = UIAlertAction(title: "Do Not Show Again", style: UIAlertActionStyle.default) { (action:UIAlertAction) in

            AlertOnce.set(true , forKey: "oneTimeAlert")
            AlertOnce.synchronize()

        }
        let change = UIAlertAction(title: "Change Branch", style: .default){(action) in
            let myVC = self.storyboard?.instantiateViewController(withIdentifier: "FindRestaurentViewControllerID") as! FindRestaurentViewController
            self.navigationController?.pushViewController(myVC, animated: true)
        }
        alertView.addAction(DoNotShowAgainAction)
        alertView.addAction(change)
        present(alertView, animated: true,completion: nil)
        alertView.setValue(NSAttributedString(string: messageTitle, attributes: [NSAttributedStringKey.font : UIFont.systemFont(ofSize: 20),NSAttributedStringKey.foregroundColor : UIColor(red: 234/256, green: 104/256, blue: 26/256, alpha: 1)]), forKey: "attributedTitle")
        alertView.view.tintColor = UIColor.black

    }
Avantgarde answered 31/5, 2018 at 10:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.