0
votes

Actually I have one view controller with 4 methods. 3 methods use to display UIAlertController with no action button like "ok" or "cancel". Title and message only. The final methods for dismissing those UIAlertController. One of my alert is show when async call is requesting. When we got the results,it will dismiss and hide.

The problem is while my first alert is displaying and suddenly connection lost and the other alert which will display about "Internet Lost" message cant display because of that warning.

2015-06-17 13:49:04.787 myauction[2404:40506] Warning: Attempt to present <UIAlertController: 0x7f8d136bafa0> on <myauction.AuctionLatestViewController: 0x7f8d13771180> while a presentation is in progress!

Which mean i cant dismiss the first alert successfully. Any help please?

Here is myAlertController

import UIKit

class MyAlertViewController:UIViewController{

var myAlertController : UIAlertController!

func displayLoadingAlert(viewController: UIViewController?) -> UIAlertController {

    var controllerToPresent = viewController
    if controllerToPresent == nil {
        controllerToPresent = self
    }

    //create an alert controller
    myAlertController = UIAlertController(title: "Loading...", message: "We are getting the data,Please Wait", preferredStyle: .Alert)

    let indicator = UIActivityIndicatorView()
    indicator.color = UIColor.redColor()
    indicator.setTranslatesAutoresizingMaskIntoConstraints(false)
    myAlertController.view.addSubview(indicator)

    let views = ["pending" : myAlertController.view, "indicator" : indicator]
    var constraints = NSLayoutConstraint.constraintsWithVisualFormat("V:[indicator]-(7)-|", options: nil, metrics: nil, views: views)
    constraints += NSLayoutConstraint.constraintsWithVisualFormat("H:|[indicator]|", options: nil, metrics: nil, views: views)
    myAlertController.view.addConstraints(constraints)

    indicator.userInteractionEnabled = false
    indicator.startAnimating()

    controllerToPresent!.presentViewController(myAlertController, animated: true, completion: nil)

    return myAlertController
}

func connectionErrorAlert(viewController: UIViewController?) -> UIAlertController {

    var controllerToPresent = viewController
    if controllerToPresent == nil {
        controllerToPresent = self
    }

    //create an alert controller
    myAlertController = UIAlertController(title: "Not Connected", message: "No Internet Connection", preferredStyle: .Alert)
    let defaultAction = UIAlertAction(title: "OK", style: .Default,handler:nil)
    myAlertController.addAction(defaultAction)

    controllerToPresent!.presentViewController(myAlertController, animated: true, completion: nil)

    return myAlertController
}

func requestTimeOutErrorAlert(viewController: UIViewController?) -> UIAlertController {

    var controllerToPresent = viewController
    if controllerToPresent == nil {
        controllerToPresent = self
    }

    //create an alert controller
    myAlertController = UIAlertController(title: "Request Time Out", message: "Please Tap Retry to Get The Data", preferredStyle: .Alert)
    let defaultAction = UIAlertAction(title: "OK", style: .Default,handler:nil)
    myAlertController.addAction(defaultAction)

    controllerToPresent!.presentViewController(myAlertController, animated: true, completion: nil)

    return colayAlertController
}

func dismissLoadingAlert(){
    myAlertController.dismissViewControllerAnimated(true, completion: nil)
}

}

Here is my viewcontroller(note i will not show initialization of myAlertViewController)

func getResults{
     api.searchCar()
     //This is where i start to load my first alert every time it call
     self.myAlertViewController = displayLoadingAlert(self)
}

func didReceiveAPIResults(results: NSDictionary,headers:JSON) {
     dispatch_async(dispatch_get_main_queue(), {
          //do about showing the results....

          //Then i dismiss my first loading alert
          self.myAlertController.dismissViewControllerAnimated(true){}
     })
}

func didNotReceiveAPIResults(results: Bool,error:NSError){
    dispatch_async(dispatch_get_main_queue(), {
        //I did dismiss the first alert before i gonna show another one
        self.myAlertController.dismissViewControllerAnimated(true){
        if (results) {
            if error.localizedDescription == "The Internet connection appears to be offline."{
                self.myAlertViewController = connectionErrorAlert(self)
                self.carTableView.hidden=true
                self.retryButton?.hidden=false
                self.retryButton?.enabled=true
            }
            if error.localizedDescription == "Request Time Out."{
                self.myAlertViewController = requestTimeOutErrorAlert(self)
                self.carTableView.hidden=true
                self.retryButton?.hidden=false
                self.retryButton?.enabled=true
            }

        }else{
            //self.myAlertController.displayLoadingAlert(self)
            self.retryButton?.hidden=true
            self.retryButton?.enabled=false
            self.carTableView.hidden=false
            self.carTableView.reloadData()
        }
      }
    })
}
2

2 Answers

2
votes

You should insert your code in the completion block of the dismissViewControllerAnimatedmethod before presenting another ViewController modally.

Take a look at your code, which I've updated:

func didNotReceiveAPIResults(results: Bool,error:NSError){
    dispatch_async(dispatch_get_main_queue(), {
        //
        // I'm calling your error message in the completion block of the
        // dismissViewControllerAnimated Method
        //
        self.myAlertController.dismissViewControllerAnimated(true) {
        if (results) {
            if error.localizedDescription == "The Internet connection appears to be offline."{
                self.myAlertController.connectionErrorAlert(self)
            }

            if error.localizedDescription == "Request Time Out."{
                self.myAlertController.requestTimeOutErrorAlert(self)
            }

            //
            // Moved this code out of your if-statements since they are called
            // no matter which case is true
            //
            self.carTableView.hidden=true
            self.retryButton?.hidden=false
            self.retryButton?.enabled=true
        } else {
            //self.myAlertController.displayLoadingAlert(self)
            self.retryButton?.hidden=true
            self.retryButton?.enabled=false
            self.carTableView.hidden=false
            self.carTableView.reloadData()
        }
        }
    })
}
1
votes

First of all,I was making a mistake because I am creating new viewcontroller for alert instead of defining extension.So,if some beginner who are searching for results,dont do like my above code.So here is the answer.

If u want to add custom alert,use extension like this.

import UIKit

extension UIAlertController {

class func displayLoadingAlert() -> UIAlertController {

    //create an alert controller
    let myAlertController = UIAlertController(title: "Loading...", message: "We are getting the data,Please Wait...", preferredStyle: .Alert)

    let indicator = UIActivityIndicatorView()
    indicator.color = UIColor.redColor()
    indicator.setTranslatesAutoresizingMaskIntoConstraints(false)
    myAlertController.view.addSubview(indicator)

    let views = ["pending" : myAlertController.view, "indicator" : indicator]
    var constraints = NSLayoutConstraint.constraintsWithVisualFormat("V:[indicator]-(7)-|", options: nil, metrics: nil, views: views)
    constraints += NSLayoutConstraint.constraintsWithVisualFormat("H:|[indicator]|", options: nil, metrics: nil, views: views)
    myAlertController.view.addConstraints(constraints)

    indicator.userInteractionEnabled = false
    indicator.startAnimating()

    return myAlertController
}

class func connectionErrorAlert() -> UIAlertController {

    let myAlertController = UIAlertController(title: "Not Connected", message: "No Internet Connection", preferredStyle: .Alert)
    let defaultAction = UIAlertAction(title: "OK", style: .Default,handler:nil)
    myAlertController.addAction(defaultAction)
    return myAlertController
}

class func requestTimeOutErrorAlert() -> UIAlertController {
    // same as above
}
}

Then how to use it from other view controllers(for displaying and dismissing)

class ViewController : UIViewController{

       var myAlert : UIAlertController!
       //For displaying the one of the alert
       func getResults(){
            myAlert = UIAlertController.displayLoadingAlert()
            self.presentViewController(myAlert, animated: true, completion: nil)
       }
       //For dismissing the alert,please add the code in the completion that will do after dismiss
      func afterGettingResults(){
           self.myAlert.dismissViewControllerAnimated(true){
               //do your job after dismiss is done
           }
      }
} 

Hope this help,everyone.Thanks to @ezCoding which try to help me get out from this dark error of mine and show me light to success.