can i call UIAlertController out of IBAction [duplicate] - ios

This question already has answers here:
Present UIAlertController from AppDelegate [duplicate]
(7 answers)
Closed 3 years ago.
I am trying to make an alert on my app, but it keeps giving me warning like the below and it's not appearing
Warning: Attempt to present <UIAlertController: 0x..> on <xyz.VC1: 0x..> whose view is not in the window hierarchy!
the logic is like this :-
the IBAction in (VC1) calls a public function (X)
(X) the function do some operation and functions and based on it it's called the public function (Alert)
(Alert) the function should present an alert, but it gives me the previous warning.
NOTE: the alert works fine if I use it directly from the IBAction
present the alert :
func WAlert(){
// print("Wrong :("") // to be an alert
let alert = UIAlertController(title: "S?", message: "Y", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "C", style: UIAlertAction.Style.default, handler: { _ in
//Cancel Action
}))
alert.addAction(UIAlertAction(title: "out",
style: UIAlertAction.Style.default,
handler: {(_: UIAlertAction!) in
//Sign out action
}))
present(alert, animated: true, completion: nil)
//self.present(alert, animated: true, completion: nil)
//VC1.present(alert, animated: true, completion: nil)

You probably need to have the function return the alert, rather than presenting it, and then present it (self.present) from VC1. As Teja Nandamuri mentions in comments, an alert must be presented from a visible UIViewController.
Revised based on comment:
You can generate the alert in a separate function, like your WAlert, but still present it in VC1, even in the IBAction. For instance, in the action you would have:
let alert = WAlert()
self.present(alert, animated: true)
You would need to change WAlert as follows:
func WAlert() -> UIAlertController {
let alert = UIAlertController(title: "S?", message: "Y", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "C", style: UIAlertAction.Style.default, handler: { _ in
//Cancel Action
}))
alert.addAction(UIAlertAction(title: "out", style: UIAlertAction.Style.default, handler: {(_: UIAlertAction!) in
//Sign out action
}))
return alert

Related

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
}

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)

How do I pop to RootViewController when an alert is dismissed?

#IBAction func addButton(sender: AnyObject) {
let alert = UIAlertController(title: "New Exercise Added", message: "\(name)", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok!!", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
self.navigationController?.popToRootViewControllerAnimated(true)
self.dismissViewControllerAnimated(true, completion: {})
}
Within the IB action function of a button I have an alert, followed by some code to change to a different ViewController.
The program crashes upon reaching these lines of code after the alert:
2016-01-04 17:48:27.147 FitnessApp[60584:4080964] popToViewController:transition: called on while an existing transition or presentation is occurring; the navigation stack will not be updated.
How do I run the code to change ViewController after the transition is done?
Your biggest issue is that you don't do anything with the alert button's handler. Instead, you immediately try to do the pop and dismiss after presenting the alert.
Move the code to pop the controller into the Ok button's alert handler.
#IBAction func addButton(sender: AnyObject) {
let alert = UIAlertController(title: "New Exercise Added", message: "\(name)", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok!!", style: UIAlertActionStyle.Default, handler: {
self.navigationController?.popToRootViewControllerAnimated(true)
// You only need the pop
//self.dismissViewControllerAnimated(true, completion: {})
}))
self.presentViewController(alert, animated: true, completion: nil)
}
Note: I'm not fluent in Swift so the syntax could be off a little.

Show alert in AppDelegate in Swift [duplicate]

This question already has answers here:
How to show UIAlertController from Appdelegate
(6 answers)
Closed 4 years ago.
I try the next code snippet:
var alert = UIAlertController(title: "Alert", message: "Cannot connect to : \(error!.localizedDescription)", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
self.window?.rootViewController?.presentViewController(alert, animated: true, completion: nil)
in my AppDelegate, but it prints me the next error in console:
Warning: Attempt to present <UIAlertController: 0x7ff6cd827a30> on <Messenger.WelcomeController: 0x7ff6cb51c940> whose view is not in the window hierarchy!
How can I fix this error?
This is what i'm using now to do that.
var alertController = UIAlertController(title: "Title", message: "Any message", preferredStyle: .ActionSheet)
var okAction = UIAlertAction(title: "Yes", style: UIAlertActionStyle.Default) {
UIAlertAction in
NSLog("OK Pressed")
}
var cancelAction = UIAlertAction(title: "No", style: UIAlertActionStyle.Cancel) {
UIAlertAction in
NSLog("Cancel Pressed")
}
alertController.addAction(okAction)
alertController.addAction(cancelAction)
self.window?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)
Swift 5:
let alert = UIAlertController(title: "Test", message:"Message", preferredStyle: UIAlertController.Style.alert)
// add an action (button)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
// show the alert
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
As per Jorge's answer, updated for Swift 4
let alertController = UIAlertController(title: "Title", message: "Message", preferredStyle: .actionSheet)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default) {
UIAlertAction in
NSLog("OK Pressed")
}
let cancelAction = UIAlertAction(title: "CANCEL", style: UIAlertActionStyle.cancel) {
UIAlertAction in
NSLog("Cancel Pressed")
}
alertController.addAction(okAction)
alertController.addAction(cancelAction)
self.window?.rootViewController?.present(alertController, animated: true, completion: nil)
Swift 3.0 or above, Working in all condition , like in case of tab bar, in case of presented view etc ..
let alert = UIAlertController(title: "Test", message:"Message", preferredStyle: UIAlertControllerStyle.alert)
// add an action (button)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
// show alert
let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.present(alertController, animated: true, completion: nil)
I had the similar problem.
I have fixed it by presenting UIAlertController in Main Queue.
Code Looks like following.
let alert = UIAlertController(title: "My Title", message: "My Message", preferredStyle: .alert)
let actionYes = UIAlertAction(title: "Yes", style: .default, handler: { action in
print("action yes handler")
})
let actionCancel = UIAlertAction(title: "Cancel", style: .destructive, handler: { action in
print("action cancel handler")
})
alert.addAction(actionYes)
alert.addAction(actionCancel)
DispatchQueue.main.async {
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
}
Have you tried using UIApplication.shared.keyWindow?.rootViewController?.present(...) ?
I suppose you are calling that code snippet from the applicationDidFinishLunchingWithOptions.
I tried it as a matter of fact because I had to. The thing is: what you are trying to do is correct but the ViewController that the AppDelegate makes and presents is about to be put on screen and before that, the code snippet tries to create an alertView and put in on top of non existent View of the RootViewController.
What I would do is move it to another delegate call which is guaranteed to be called after the RootViewController is presented.
func applicationDidBecomeActive(application: UIApplication) {
//This method is called when the rootViewController is set and the view.
// And the View controller is ready to get touches or events.
var alert = UIAlertController(title: "Alert", message: "Cannot connect to :", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
self.window?.rootViewController?.presentViewController(alert, animated: true, completion: nil)
}
But as always know the responsibility of the AppDelegate. It is to handle the application lifecycle and application wide delegate calls and events. If putting code here makes sense, then do it. But if you will be better off putting the code on the rootViewController or other parts then think about it too.
Anyway, hope it helps. Cheers!
I would suggest NOT doing this in the AppDelegate. The App Delegate it intended to handle Delegate functions from the OS rather than implementing things like alert views.
If you are wanting to present an alert view here to be shown at the start of the app I would do this by implementing it in your first view controller.

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