Unable to put two AlertControllers on same VC - ios

I have a screen with a collection view of items. If the user has not selected anything, I want an alert to pop up, prompting them to choose something. If they have chosen something, I want an alert to pop up, asking if they are ready to move on? Below is my code for this:
if (isSelected) {
// create the alert
let alert = UIAlertController(title: "Create", message: "Make sure to select at least one item.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Cancel, handler: { action in
alert.dismissViewControllerAnimated(true, completion: nil)
}))
self.presentViewController(alert, animated: true, completion: nil)
} else {
let alert2 = UIAlertController(title: "Move on", message: "Are you ready to move on?", preferredStyle: UIAlertControllerStyle.Alert)
alert2.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.Default, handler: { action in
self.performSegue to next screen
}))
n.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.Cancel, handler: { action in
}))
}
The code seems to be fine but i get the following error:
Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior
This seems like it should work pretty easily and a common thing to do, but there is no solution on line to this problem. Any help/guidance would be much appreciated.

you had missed to present the alert2
self.presentViewController(alert2, animated: true, completion: nil)
Add that and it will work fine.

This line is not required:
alert.dismissViewControllerAnimated(true, completion: nil)
Also add
self.presentViewController(alert, animated: true, completion: nil)
in else part.

Related

UIAlertController: How to make the right button bolded?

When I run the following code, the Yes button is always on the left side, how can I change it to right side and keep it bolded?
func showInAppPurchaseAlert() {
let alertController = UIAlertController.init(title: "Upgrade?", message: "Do you want to upgrade to pro version?", preferredStyle: .alert)
alertController.addAction(UIAlertAction.init(title: "No", style: .default, handler: { action in
self.dismiss(animated: true, completion: nil)
}))
let actionUpgrade = UIAlertAction.init(title: "Yes", style: .cancel, handler: { action in
self.upgradeToPro()
})
alertController.addAction(actionUpgrade)
alertController.preferredAction = actionUpgrade
self.present(alertController, animated: true, completion: nil)
}
The .default style will always have the right side, and the .cancel style the bold text. In order for you to change that, you have to set the preferredAction like you did in your code, but the problem was that you were using .cancel on your "Yes" action, which you wanted on the right side and your "No" action to .default. I tried your code, and this version works for me with your requirements.
func showInAppPurchaseAlert() {
let alertController = UIAlertController.init(title: "Upgrade?", message: "Do you want to upgrade to pro version?", preferredStyle: .alert)
alertController.addAction(UIAlertAction.init(title: "No", style: .cancel, handler: { action in
self.dismiss(animated: true, completion: nil)
}))
let actionUpgrade = UIAlertAction.init(title: "Yes", style: .default, handler: { action in
self.upgradeToPro()
})
alertController.addAction(actionUpgrade)
alertController.preferredAction = actionUpgrade
self.present(alertController, animated: true, completion: nil)
}
Pass .default for style:, it will use the cancel button look and feel.

alert completion not fully working

I have an alert and right after the alert is shown, I would like to present a different viewFinder. The doSomething() function is fired, "TEST" is printed, but the new viewfinder is not presented. What am I missing?
Alert
let alert = UIAlertController(title: "Sorry", message: "Booked out.",
preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style:
UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: self.doSomething)
content func doSomething()
print("TEST")
let details = storyboard?.instantiateViewController(withIdentifier: "ViewLimo2")
details?.transitioningDelegate = slideAnimatorRight
present(details!, animated: true, completion: nil)
The completion block on a view controller doesn't fire when the view controller is dismissed. It fires when the view controller finishes presenting (e.g. it has finished with viewDidAppear).
Honestly, I'd expect this to crash, since you're attempting to present while the alert is still presenting.
In any case, you need to wait until the dismissal of the UIAlertController before you try to present the next View Controller.
You could do it in the handler for the OK action:
let alert = UIAlertController(title: "Sorry", message: "Booked out.",
preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style:
UIAlertActionStyle.default, handler: doSomething))
self.present(alert, animated: true, completion: nil)
...
func doSomething(action:UIAlertAction) {
/// present the next VC here
}

How can I perform a segue after receive an alert answer

Today I'd like to try to make a part of an application that after show an alert perform a segue ONLY if the user press on the "Yes" alert's button.
To explain me better I'd like that the application will show an aller that say "Are you sure to return to Home?" and it will have two Botton: "Yes" and "No". If the user press no nothing happen, if the user press yes the application perform a segue. The problem is that I don't know how to do it.
I try to write some code line but it doesn't work.
func Alert (TITLE: String, MESSAGE: String) -> Bool()
{
var X = false
let Alert = UIAlertController(title: TITLE, message: MESSAGE, preferredStyle: UIAlertControllerStyle.alert)
Alert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.default, handler: { (Action) in
X = true
Alert.dismiss(animated: true, completion: nil)
}))
self.present(Alert, animated: true, completion: nil)
Alert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default, handler: nil))
self.present(Alert, animated: true, completion: nil)
return X
}
#IBAction func ButtonAct(_ sender: Any) //This happen if you click the botton on the screen of the iPhone
{
if Alert (TITLE: "Return to Home", MESSAGE: "Are you sure to return Home?")
{performSegue(withIdentifier: "Segue", sender: self)}
}
Thank you for you Help
There are several problems with your code:
No part of your code is calling the #IBAction method, that is why the segue is not being performed. The alert controller only provides you with completion handlers; there is no target/action mechanism like with UIButton.
The boolean return value is determined inside the action's completion handler, asynchronously. Your function returns right away the value false, before the user has had a chance to chose. Things happen in this order:
Call alert()
Function presents UIAlertController
Function returns false
(very many milliseconds pass)
User taps "YES"
Completion handler is executes and x is set to true, but the alert() method has already returned false.
You are presenting the alert twice, once after adding each action. You need to add both actions and then present it only once.
Please use standard capitalization in your code.
Try something like this:
func alert (title: String, message: String, completion: ((Bool) -> Void)) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alertController.addAction(UIAlertAction(title: "Yes", style: .default, handler: { (action) in
alertController.dismiss(animated: true, completion: nil)
completion(true) // true signals "YES"
}))
alertController.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default, handler: { (action) in
alertController.dismiss(animated: true, completion: nil)
completion(false) // false singals "NO"
}))
self.present(alertController, animated: true, completion: nil)
}
...and call it like this:
alert(title: "Hi", message: "Want to proceed?", completion: { result in
if result {
self.performSegue(withIdentifier: "Segue", sender: self)
}
})

Trying to present 2 UIAlertControllers back to back

I have a UIAlertController in which the options are populated from an array and are presented to the user. The user then selects an option from the alert. After this, I have a separate alert that provides the user with a confirmation message that has an okay button.
myAlert.addAction(UIAlertAction.init(title: item, style: .Default, handler: {
(UIAlertAction) in
self.chosenBusiness.append(businessNameData[item]!)
}))
self.presentViewController(myAlert, animated: true, completion: nil)
The code above gathers the data from the array and pushes it into actions in myAlert. The code above is inside of a for loop.
After this I use a function to retrieve the topmost view controller, and then push the next alert.
let top = topMostController()
let alertController = UIAlertController(title: "Location pinned", message: "You've successfully pinned this location, good work!", preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default) {
(result : UIAlertAction) -> Void in
print("OK")
}
alertController.addAction(okAction)
self.presentViewController(myAlert, animated: true, completion: nil)
top.presentViewController(alertController, animated: true, completion: {
_ in
})
The error I receive is:
Attempting to load the view of a view controller while it is
deallocating and is not allowed and may result in undefined behavior.
UIAlertController: 0x1535b1cd0.
Can someone help me with this?
I think this is what you are looking for. The second must be called with the dismissal action of the first. Also, anytime you work with UI, It is safer to use dispatch_async(dispatch_get_main_queue()) {
\\code }
than not if you are not positive you are currently on the main queue.
let firstAlertController = UIAlertController(title: "First", message: "This is the first message.", preferredStyle: UIAlertControllerStyle.Alert)
let secondAlertController = UIAlertController(title: "Second", message: "This is the second message.", preferredStyle: UIAlertControllerStyle.Alert)
let secondDismissAction = UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, completion: nil)
secondAlertController.addAction(secondDismissAction)
let firstDismissAction = UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default) {
UIAlertAction in
dispatch_async(dispatch_get_main_queue()) {
self.presentViewController(secondAlertController, animated: true, handler: nil)
}
}
firstAlertController.addAction(firstDismissAction)
self.presentViewController(firstAlertController, animated: true, completion: nil)

Issues with displaying Alerts in Swift (After clicking "Ok", it unwinds segue

I wrote the following function below to display alerts through my project. Every time i check for an error in a form i display the alert when the user clicks submit. I want to simply show the alert, but not unwind the pervious segue. I want to stay at the current screen and give the user the opportunity to complete the form. Right now when the user clicks submit, it displays the alert ..but when i click the ok button to dismiss the alert it immediately unwinds the segue to the previous screen.
I have included the UIAlertViewDelegate in the class.... any ideas why this might be happening?
func displayAlert(title:String, error:String) {
var alert = UIAlertController(title: title, message: error, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: { action in
self.dismissViewControllerAnimated(true, completion: nil)
}))
self.presentViewController(alert, animated: true, completion: nil)
}
Never mind.
func displayAlert(title:String, error:String) {
var alert = UIAlertController(title: title, message: error, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: { action in
//comment out this line and it will still work. It will not segue back to the previous screen.
//self.dismissViewControllerAnimated(true, completion: nil)
}))
self.presentViewController(alert, animated: true, completion: nil)
}

Resources