My understanding is that normal behavior for a UIAlertController after being presented is to wait for the user to respond in other words pause other code.
In this case when the user clicks save, a UIAlertController is supposed to appear, ask the user a question about saving and then wait for the response. However, the code is continuing and the alert controller gets dismissed after about one second.
What could be wrong with the following that is preventing the code from pausing and causing the alert to disappear after about one second?
-(void) save {
if (listView.text.length>=1) {
[self fireAlertNewOrUpdate];
}
// save everything else
//dismiss view controller - this line of code may be dismissing the alert controller...
}
-(void) fireAlertNewOrUpdate {
UIAlertController *listAlert = [UIAlertController alertControllerWithTitle:#"Save list as new?" message:nil preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* yes = [UIAlertAction
actionWithTitle:#"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[self saveNewList];
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:#"Update existing" style:UIAlertActionStyleCancel handler:^(UIAlertAction * action)
{
[self updateExisting];
}];
[listAlert addAction:cancel];
[listAlert addAction:yes];
if ([listAlert respondsToSelector:#selector(setPreferredAction:)]) {
[listAlert setPreferredAction:yes];
}
[self presentViewController:listAlert animated:YES completion:nil];
}
You've given the answer yourself, except that you've also hidden it from us. (Fortunately, you hinted at it in a comment.) It's because there's a line of code you've omitted, in save, where you dismiss the view controller. That view controller is the alert, so the alert appears in response to the call fireAlertNewOrUpdate and then immediately disappears again. In effect, you are saying present / dismiss in a single breath.
My understanding is that normal behavior for a UIAlertController after being presented is to wait for the user to respond in other words pause other code
No, not at all true. In fact, just the opposite. Your "other code" just goes right on even after the alert has appeared. There is basically nothing in iOS programming where code will spontaneously "pause". For this reason, it is quite usual after you call present to present a view controller to do nothing further in that code.
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Ok. So I'm working on a game in sprite kit using objective C. I have a method that I call that uses the viewcontroller the skscene is running from to call a uialertcontroller at game over. Upon pressing the ok button in the alert view, I modal back to the main menu view controller. This works fine. However when I go to play the game again, switching back to the gameviewcontroller and get another game over, the uialertview fails to trigger. I get an error message saying:
Warning: Attempt to present <UIAlertController: 0x14205d600> on <GameViewController: 0x141e176f0> whose view is not in the window hierarchy!
Here is the code where I call the UIAlertController:
UIAlertController * gameOverAlert = [UIAlertController alertControllerWithTitle: #"Game Over!" message: textscore preferredStyle: UIAlertControllerStyleAlert];
//add the button that will take us back to the main menu
UIAlertAction *okButton = [UIAlertAction actionWithTitle:#"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
[UIApplication.sharedApplication.keyWindow.rootViewController.presentedViewController performSegueWithIdentifier:#"backToTheMenuNotFuture" sender:self];
}];
[gameOverAlert addAction: okButton];
[UIApplication.sharedApplication.keyWindow.rootViewController.presentedViewController presentViewController: gameOverAlert animated:true completion: nil];
Here is the code for when I modal to the gameviewcontroller:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"gotoPlay"]){
gameController = (GameViewController *)segue.destinationViewController;
gameController.playingMusic = musicHave;
}
}
As I said, this work fine the first game, but when you go to play again and lose a second time, that's when the error happens.
Solved:
(isDismissed is an int value starting off as 0)
//add the button that will take us back to the main menu
UIAlertAction *okButton = [UIAlertAction actionWithTitle:#"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action){
isDismissed = 1;
[UIApplication.sharedApplication.keyWindow.rootViewController.presentedViewController performSegueWithIdentifier:#"backToTheMenuNotFuture" sender:self];
}];
[gameOverAlert addAction: okButton];
[UIApplication.sharedApplication.keyWindow.rootViewController.presentedViewController presentViewController: gameOverAlert animated:true completion: nil];
if(isDismissed == 1){
[UIApplication.sharedApplication.keyWindow.rootViewController.presentedViewController dismissViewControllerAnimated:NO completion: nil];
[UIApplication.sharedApplication.keyWindow.rootViewController.presentedViewController presentViewController: gameOverAlert animated:true completion: nil];
[UIApplication.sharedApplication.keyWindow.rootViewController.presentedViewController performSegueWithIdentifier:#"backToTheMenuNotFuture" sender:self];
}
}
So the issue here as in the comments is that there were multiple gameviewcontroller instances running at once. The code above dismisses the 'current' gameviewcontroller and then attempts to run what I want to the original one. It works, however I am unsure of whether or not the game is then deallocating the old gameviewcontroller or creating yet more duplicates every time it runs. In the end I see it as this. If it is deallocating like it should then its fine. If its not, then the game which I'd say consumes about 8MB of RAM would eventually cause the iPhone to lag. If this does happen then you have obviously been playing it for so long that you should really take a break from it anyway. Its not a bug, its a feature!
I need every single UIAlertController in my app to do a certain action whenever a user pushes a button on it (i.e. when it's dismissed). As of now, the only way I can think to do it is by individually coding this action into each of buttons handlers like this:
UIAlertAction *cancel = [UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action)
{
[self doSomeAction];
}];
There is no delegate for UIAlertController. How can I accomplish the same action being done every time an alert is dismissed without having to repeat the same code over and over?
The simplest solution for this is to subclass UIAlertController. Then override viewDidDisappear. Add whatever "one specific thing" you want to happen for every alert there. Now use your custom alert controller class whenever you want this custom behavior to be done.
I have the situation where the alert is showing up in the upper left-hand corner of the screen (and cut off), as described in
UIAlertController is moved to buggy position at top of screen when it calls `presentViewController:`
My code is copy and paste from Apple's documentation:
- (void) alertHere
{
UIAlertController* alert = [UIAlertController
alertControllerWithTitle:#"My Alert"
message:#"This is an alert."
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[alert addAction:defaultAction];
[self presentViewController:alert animated:YES
completion:nil];
}
I am calling this from a UIViewController.
I simply don't understand the stackoverflow discussion referenced above, nor the answers. I need something a poor simple programmer from steppes can understand.
Apple doesn't consider this a bug (apparently), but I don't understand why and I certainly don't understand how to fix it.
Thank you for a nice simple solution, or at least a simple description of what I'm doing wrong.
According to this post Attempt to present UIViewController on UIViewController whose view is not in the window hierarchy It seems you call alertHere from viewDidLoad so for solved your problem you must call it in ViewDidAppear.
The answer lies here: How to present UIAlertController when not in a view controller?
It took my a while to understand it, but once implemented, it works.
We used to use UIAlertViewDelegate in ViewController.h to make our custom Alert button to do an action, like pressing the Play Again button in the alert window makes the game restart.
How do we do this with UIAlertController, the replacement class for UIAlertView?
I read this document bellow provided by Apple and didn't see a delegate method mentioned. Does it mean we don't do that anymore?
https://developer.apple.com/reference/uikit/uialertcontroller
First add UIAlertController
Then Add UIAlertAction
Then add that action to your alert controller.
like below.
UIAlertController *myalert = [UIAlertController alertControllerWithTitle:#"your title" message:#"your messate" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *myaction = [UIAlertAction actionWithTitle:#"you title" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
//your action here
}];
[myalert addAction:myaction];
[self presentViewController:myalert animated:YES completion:nil];
As it sounds like you found out, UIAlertView is deprecated (beginning in iOS 8), and so is UIAlertViewDelegate.
You're supposed to use UIAlertController instead. UIAlertController uses a different method for handling user taps on buttons. In UIAlertController you add UIAlertAction objects to your alert controller.
A UIAlertAction takes a block of code that gets called when the user triggers that action. Instead of setting up a delegate that listens for messages in a protocol, you pass a handler block (closure) to each button action that you set up. It's a different way of solving the same problem.
#AnuradhS gave sample code showing hot to set it up.
I seem to be running into a problem similar to one in an unresolved posted question: UIAlertController dismissing his presentingViewController
I am presenting a modal view controller on top of a normal UIViewController. Then I'm popping up an alert on that modal view controller. When I push "ok" to dismiss the alert (generated with the code below), the modal view controller is also dismissed.
UIAlertAction *ok = [UIAlertAction actionWithTitle:#"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action{
[self dismissViewControllerAnimated: YES completion: nil];}];
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Sign up problem."
message:#"Some fields are empty. Please check your inputs and try again."
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:ok];
[self presentViewController:alert animated:YES completion:nil];
How can I dismiss just the alert?
I realize I can avoid this problem by using a navigation controller type setup instead and hiding the navigation bar, so I Get the same feel as the modal view controller, but this seems silly. Thanks.
Don't call self dismissViewController in the button handler. That specifically states that you want the view controller dismissed.
You don't need to dismiss the alert. It will automatically dismiss itself. The only thing you should do in the button handler is perform whatever action you need. Do nothing if you don't need to do anything.
If your alert is simply a message and you don't need to perform any action, just do this:
UIAlertAction *ok = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:nil];
You don't need to dismiss or remove the UIAlertController manually in any way in a button handler - it does that itself.
Just remove the call to dismissViewControllerAnimated:completion:.