I have a login screen that raises an alert when login fails. The code that calls the alert is run from a callback closure which itself calls a function in the main VC.
_ = UserProfile.logIn(emailAddressLabel.text!, passwordLabel.text!) { success in
if success {
self.performSegue(withIdentifier: "mainTabBarSegue", sender: nil)
}
else {
self.displayFailedLoginAlert()
}
self.loggingIn = false
}
and the displayFailedLoginAlert looks like this:
func displayFailedLoginAlert () {
let alert = UIAlertController(title: "Login", message: "Login Failed", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.default, handler: { _ in
alert.dismiss(animated: false, completion: nil)
}))
self.present(alert, animated: false, completion: nil)
}
However, when I do this I get:
Warning: Attempt to present <UIAlertController: 0x7ff8fd0b5800> on <LoginViewController: 0x7ff8fcc0deb0> which is already presenting <UIAlertController: 0x7ff8fe0cca00>
I have tried a number of different approaches and either get this or a crash if I use a UIAlertController as a class member. What am I doing wrong, I just can't see it?
You don't need to tell the alert to dismiss at all. The default behavior when tapping an action in a UIAlertController is that the alert will dismiss. Just pass nil to the handler.
The issue was that, each time the user logged in I added an observer to the API call. So, on the second login attempt the closure was called twice and so raised the error. Thanks to Frizzo for pointing me in the right direction
Related
I am trying to completely deallocate my view controller from memory. After hours of testing, I've finally narrowed it down to a UIAlertController staying in memory which keeps my view controller from deallocating.
#objc func logout_click() {
let alert = UIAlertController(title: "Confirmation", message: "Are you sure you want to log out?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "YES", style: .default, handler: { _ in
// 'YES' button action
do {
try Auth.auth().signOut()
self.popInit()
} catch {
print(error)
}
}))
alert.addAction(UIAlertAction(title: "NO", style: .default, handler: { _ in
// 'NO' button action
alert.dismiss(animated: true)
}))
self.present(alert, animated: true)
}
func popInit() {
//Go back to init screen
self.navigationController?.popToRootViewController(animated: true)
}
As long as this alert doesn't show, I can use popInit() and my view controller deallocates just fine, but after this alert shows up, even after dismissing, the view controller will not deallocate. I am not referencing any variables outside the scope of this function, so why does this not allow me to deallocate? What do I need to do to allow my view controller to deallocate?
Have the YES action handler declare [weak self] and call self?.popInit().
Also, as suggested in a comment, you can replace the NO handler with nil.
I'm creating a function in my Login View controller. Where if the users password or email Address is incorrect he gets an alert saying 'Login not Successful'. But if the users password and email matches he gets to segue to the Dashboard view controller.
func alert(response:String) {
let alert = UIAlertController(title: "Login Failure", message: "Incorrect Email Address, Phone Number or Password", preferredStyle: .alert)
if response == "Login in not sucessful" {
alert.addAction(UIAlertAction(title:"Okay", style: .cancel,handler:nil))
alert.dismiss(animated: true, completion: nil)
self.present(alert, animated: true, completion: nil)
} else if response == "Login Successful" {
self.performSegue(withIdentifier: "dashboardSegue", sender: self)
}
But whenever I tap the Log in button I either get one of these errors:
Warning: Attempt to present <myApp.customTabBar: 0x7fe68a889800> on <myApp.ViewController: 0x7fe68a606f90> while a presentation is in progress!
Warning: Attempt to present <UIAlertController: 0x7fe68b853e00> on myApp.ViewController: 0x7fe68a606f90> while a presentation is in progress!
Warning: Attempt to present <UIAlertController: 0x7fe68b815400> on <myApp.ViewController: 0x7fe68a475240> whose view is not in the window hierarchy!
the alert shows only once and the app segues anyways to the dashboard viewcontroller even though the users email and password are incorrect. I'm thinking maybe its the if-condition statement. I don't whats wrong. I've checked other questions but I can't quite use the viewDidAppear method because I don't want the alert to appear on startup. Newbie Here , Thanks for any help !!!
Change following line of code will fix your issue.
alert.addAction(UIAlertAction(title:"Okay", style: .cancel,handler:nil))
alert.dismiss(animated: true, completion: nil)
self.present(alert, animated: true, completion: nil)
To
alert.addAction(UIAlertAction(title:"Okay", style: .cancel, handler:nil))
self.present(alert, animated: true, completion: nil)
I'm new to Swift programming, but can't find an answer to my problem, which is...
When I present a simple UIAlertController with a UIAlertAction handler, I am expecting the alert to display until the user responds, then the handler is executed, before continuing with the remaining code.
Unexpectedly, it seems to finish off the code block before displaying the alert and executing the handler.
I've searched Stackoverflow, and re-read the Apple Developer Documentation for UIAlertController and UIAlertAction, but I can't figure out why the code doesn't pause until the user responds.
I've tried putting the UIAlertController code in its own function, but the alert still appears to be displaying out of sequence. I'm thinking maybe there needs to be a delay to allow the Alert to draw before the next line of code executes(?).
#IBAction func buttonTapped(_ sender: Any) {
let alert = UIAlertController(title: "Ouch", message: "You didn't have to press me so hard!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Sorry", style: .default, handler: { _ in
self.handleAlert()
}))
self.present(alert, animated: true, completion: nil)
print("Should be printed last!")
}
func handleAlert() {
print("UIAlertAction handler printed me")
}
In the code above I am expecting the debug console to display:
UIAlertAction handler printed me
Should be printed last!
But instead it displays:
Should be printed last!
UIAlertAction handler printed me
Instead of adding a seperate function, can you put it within the alert action itself like this...
let alert = UIAlertController(title: "Ouch", message: "You didn't have to press me so hard!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Sorry", style: .default, handler: { action in
// code for action goes here
}))
self.present(alert, animated: true)
UIAlertController is designed to run asynchronously (that is why it has you pass a block of code to execute when the action is performed instead of giving a return value)
So to fix your code, put the code you want to run after an action is chosen in another function, then call that function at the end of each UIAlertAction handler.
private var currentlyShowingAlert = false
#IBAction func buttonTapped(_ sender: Any) {
if currentlyShowingAlert {
return
}
let alert = UIAlertController(title: "Ouch", message: "You didn't have to press me so hard!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Sorry", style: .default, handler: { _ in
self.handleAlert()
self.alertCleanup()
}))
self.present(alert, animated: true, completion: nil)
currentlyShowingAlert = true
}
func handleAlert() {
print("UIAlertAction handler printed me")
}
func alertCleanup() {
print("Should be printed last!")
currentlyShowingAlert = false
}
Be careful when doing things like pushing view controllers (or anything where the calls will stack up) in direct response to a button press.
When the main thread is busy, the button can be pressed multiple times before the first buttonTapped call happens, in that case buttonTapped could be called many times in a row, currentlyShowingAlert will prevent that issue.
I have a game which displays an alert whenever a player wins. However after restarting the game and the same alert shows up multiple 'cancel' buttons show up. just like showed in the picture. any ideas what it could be,
var alertX = UIAlertController(title: "Winner", message: "X Has Won", preferredStyle:.alert)
func AlertPlayer1() {
alertX.addAction(UIAlertAction(title:"CLOSE",style: UIAlertAction.Style.destructive, handler: { (action) in self.alertX.dismiss(animated: true, completion: nil)}))
self.present(alertX, animated:true, completion:nil)
}
I have simply then just called the function whenever somebody wins
Please update your code as following to fix issue.
func AlertPlayer1() {
var alertX = UIAlertController(title: "Winner", message: "X Has Won", preferredStyle:.alert)
alertX.addAction(UIAlertAction(title:"CLOSE",style: UIAlertAction.Style.destructive, handler: { (action) in
self.alertX.dismiss(animated: true, completion: nil)
}))
self.present(alertX, animated:true, completion:nil)
}
You are creating alert instance single time, but this method AlertPlayer1 call multiple time from somewhere in your code which are adding multiple close button.
Note: As per I already told you, you method calling multiple time. So this alert also try to present multiple time, but at a time your can present only one view controller in window/screen. So it will show you warning in console.
I have this function and I use it on almost all viewController´s that I have, for some reason went I push "ok" to dismiss the pop up, it switches to another viewController.
this is my function...
extension UIViewController
{
func displayAlert(title: String, message: String)
{
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction((UIAlertAction(title: "Ok", style: .Default, handler:
{ (action) -> Void in
self .dismissViewControllerAnimated(true, completion: nil)
})))
self.presentViewController(alert, animated: true, completion: nil)
}
}
any help? I'm using Xcode 7.0 beta 6 with swift 2
You don't need to dismiss the alert controller yourself. That happens automatically with your alert action. You're simply providing a closure that gets called when that "OK" action gets triggered. Just remove this line:
self.dismissViewControllerAnimated(true, completion: nil)