Search code examples
iosswiftsegueuialertcontroller

UIAlertController keeps segue from performing ("window is not equal to view’s window“)


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?


Solution

  • 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)
            })
        }
    }