3

I've had similar problems in the past with UIAlertController in the sense that there's always a lag on the UI thread after the UIAlertController is dismissed.
My problem right now is that I want to perform a segue if the user clicks on the "Okay" UIAlertAction and nothing happens if "Cancel" UIAlertAction is pressed.
This is my code:

// create the uialertcontroller to display
let alert = UIAlertController(title: "Do you need help?",
                              message: "Would you like to look for a consultant?",
                              preferredStyle: .alert)
// add buttons
let okay = UIAlertAction(title: "Yes, please.", style: .default, handler: {_ in
    self.performSegue(withIdentifier: "segue", sender: nil)
})
let no = UIAlertAction(title: "No, I'm okay.", style: .cancel, handler: nil)
alert.addAction(okay)
alert.addAction(no)
self.present(alert, animated: true, completion: nil)

What is currently happening is when I tap "Okay" the segue is performing, but I can only see the last moments of the transition (i.e the animation starts while the UIAlertController is being dismissed).
How do I make the segue start once the UIAlertController is dismissed?

Note - I prefer not solving this in a hacky method like performing the segue after a fixed delay if there's another way.

Thanks!

uti0mnia
  • 276
  • 6
  • 17
  • @matt so there isn't really a way to do this unless I animate after `n` seconds? Also - no where did I say forbid – uti0mnia Nov 01 '16 at 19:56

1 Answers1

8

The problem is in this code:

let okay = UIAlertAction(title: "Yes, please.", style: .default, handler: {_ in
    self.performSegue(withIdentifier: "segue", sender: nil)
})

The handler: is not a completion handler. It runs before the alert is (automatically) dismissed. Thus, you are starting the segue while the alert is still present.

If you prefer not to use delay (though I see nothing wrong with that approach), what I would try is this:

let okay = UIAlertAction(title: "Yes, please.", style: .default, handler: {_ in
    CATransaction.setCompletionBlock({
        self.performSegue(withIdentifier: "segue", sender: nil)
    })
})
Community
  • 1
  • 1
matt
  • 447,615
  • 74
  • 748
  • 977
  • I figured - is there any way to perform this after the controller is dismissed? – uti0mnia Nov 01 '16 at 19:57
  • how did I tell you not to? If you're referring to my Note, it just means I of course prefer a method that doesn't have a static dispatch after time :) – uti0mnia Nov 01 '16 at 20:03
  • fyour answer doesn't contain a dispatch after so I don't understand why wanting to learn a good solution is unfair? – uti0mnia Nov 01 '16 at 20:13
  • 2
    To be fair I said a hacky method. And I would argue that performing something after a hard coded amount of time isn't good practice when there's a method that calls an action after*something* happens. Anyways thanks for the help – uti0mnia Nov 01 '16 at 20:18
  • 2
    I don't see why one approach is more or less "hacky" than the other. Either way, you are working around a (strange) limitation of the API. Any time you have to do that, it's a hack (and a flaw in the API). But what that means is you should file a bug with Apple, not tell me what kind of answer to give. – matt Nov 01 '16 at 20:20
  • 1
    This is an elegant solution. I prefer this to using a delay also. – Timo Feb 28 '18 at 22:50
  • @matt I have used your code posted above, and my unwindsegue is not performed. I am sure it's called because if segue.identifier is wrong it will throw an error. – bibscy Mar 14 '18 at 12:36