0
votes

After adding a UIAlertController to my app to show a progress dialog while it's performing some set up, the segue isn't performing the switch to the second ViewController anymore (see question here - turns out the culprit isn't what I first thought it was, that's why I'm opening a new question).

So now I set up a test project using Swift 5, Xcode 10 and iOS 12 and I came across the same problem there too:

Description:

  • ViewController1: Has a "Login" button that calls onClickLoginButton
  • NavigationController: Connected to VC1 via "segue12" (I dragged from the yellow "ViewController" icon)
  • ViewController2: I added the NC to it via "Embed in"; has a UITableView
  • Clicking on the "Login" button displays a UIAlertController and should then perform the segue to VC2. Instead it only outputs a message in the console (see "console output" below) and keeps VC1 loaded.

Storyboard:

enter image description here

ViewController1:

class ViewController: UIViewController {

    @IBOutlet weak var loginButton: UIButton!

    var alert:UIAlertController!
    var alertMessage:String = ""

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    @IBAction func onClickLoginButton(_ sender: Any) {
        setUpDialog()
    }

    private func setUpDialog() {
        alertMessage = "Logging in"
        alert = UIAlertController(title: "Please wait", message: alertMessage, preferredStyle: UIAlertController.Style.alert)
        self.present(alert, animated: true, completion: nil)

        performSegue(withIdentifier: "segue12", sender: self)
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        print("prepare 1->2a")
        let navVC = segue.destination as? UINavigationController
        let tableVC = navVC?.viewControllers.first as! ViewController2
        tableVC.test = "Test!"
        print("prepare 1->2b")
    }
}

ViewController2:

class ViewController2: UIViewController, UITableViewDataSource, UITableViewDelegate {

    var test:String = ""

    override func viewDidLoad() {
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
        return cell
    }
}

Console output:

prepare 1->2a
prepare 1->2b
2019-05-21 13:09:16.981738+0200 SegueTest[3505:69382] <UIView: 0x7fc40fd0e790; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x600000dc3da0>>'s window is not equal to <UIAlertController: 0x7fc41180ba00>'s view's window!

Adding this to prepare (right before print("prepare 1->2b")) removes the dialog but it still outputs the message and doesn't switch to the second view:

alert.dismiss(animated: true, completion: nil)

What does the above message mean (what's causing it?) and how do I fix this test app, so it displays the dialog, then switches to ViewController2?

1
You shouldn't do that. If you want some please wait thing use a proper progress kinda view (there are many in cocoa pods) instead of AlertController. I don't thing ALertCOntrollers suppose to do that. If you are actually doing some loading before presenting the second view you should use some progress view with completionhandler so you can properly dismiss the progress view and open the second VC.RJE
Unfortunately no proper progress view is built in. I want to use as few extra libraries as possible and there's no point to reinvent the wheel by creating my own view that is animated,... if AlertController is already a built in thing. You can use them without buttons (there's no error message/warning), you can change their text at runtime (do it from the main/ui thread!) and there's a built in function to dismiss it and do something else once it's gone (see the code Sh_Khan posted below). It might be a bit unconventional but it works and performance is good, so why not. ;)Neph

1 Answers

1
votes

You can't present an alert and do a segue at the same time use DispatchQueue.main.asyncAfter to mook a waiting

private func setUpDialog() {
    alertMessage = "Logging in"
    alert = UIAlertController(title: "Please wait", message: alertMessage, preferredStyle: UIAlertController.Style.alert)
    self.present(alert, animated: true, completion: nil)
    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
        self.alert.dismiss(animated: true, completion: {
            self.performSegue(withIdentifier: "segue12", sender: self)
        })
    }
}