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
}
Related
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
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)
#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.
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.
I have a view controller which contains a sub-view. And inside the sub-view class, I may need to pop out an alert when some condition is satisfied.
class GameViewController: UIViewController {
#IBOutlet var gameBoardUIView: GameBoardUIView
...
}
class GameBoardUIView: UIView {
...
func move() {
if !gameBoard.checkNextMoveExist() {
var alert = UIAlertController(title: "Game Over", message: nil, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Take Me Back", style: UIAlertActionStyle.Cancel, handler: {(action: UIAlertAction!) in
println("Taking user back to the game without restarting")
}))
alert.addAction(UIAlertAction(title: "New Game", style: UIAlertActionStyle.Destructive, handler: {(action: UIAlertAction!) in
println("Starting a new game")
self.restartGame()
}))
// This is where the question is
// self.presentViewController(alert, animated: true, completion: nil)
}
}
}
As you can see from the code, I cannot call presentViewController function to show the alert because my sub view is not a controller class. Should I somehow create a week reference to the parent controller inside the sub-view? What would be the best practice to implement such reference?
Swift 4
Just present the alert in UIApplication windows in root view controller:
let title = "Some title"
let message = "body message"
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
let action = UIAlertAction(title: "Aceptar", style: .cancel, handler: nil)
alert.addAction(action)
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil)
there are couple of ways how you can catch a UIViewController in your UIView.
you can make any of your view controllers as a delegate to show an alert;
you can pass a view controller's reference to your view; and
in general you can always grab the rootViewController anywhere in your code.
you need to call the dismissViewControllerAnimated(_: completion:) on the same view controller when you'd like to dismiss your alert later.
thus, I'd do such a quick solution for your case:
func move() {
if !gameBoard.checkNextMoveExist() {
let rootViewController: UIViewController = UIApplication.sharedApplication().windows[0].rootViewController
var alert = UIAlertController(title: "Game Over", message: nil, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Take Me Back", style: UIAlertActionStyle.Cancel, handler: {(action: UIAlertAction!) in
rootViewController.dismissViewControllerAnimated(true, completion: nil)
println("Taking user back to the game without restarting")
}))
alert.addAction(UIAlertAction(title: "New Game", style: UIAlertActionStyle.Destructive, handler: {(action: UIAlertAction!) in
rootViewController.dismissViewControllerAnimated(true, completion: nil)
println("Starting a new game")
self.restartGame()
}))
rootViewController.presentViewController(alert, animated: true, completion: nil)
}
}