UIAlertController in wrong position - ios

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.

Related

Why is UIAlertController not pausing code while it waits for answer?

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.

Issue with calling method a second time after switching view controllers [closed]

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!

If <UIAlertViewDelegate> is deprecated, how can I make my Alert button to do an action?

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.

dismiss UIAlertController presented by a modal view controller

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:.

Replace button in UIActionSheet with UIView

I want to replace one of the buttons in my UIActionSheet with a UIView. I am integrating Facebook into my app, and the log in button (FBLoginView) is a subclass of UIView. I want use this view as a button in a UIActionSheet. Is this possible?
Edit: I am trying to integrate facebook sharing in my app. For that, i hacve to use their sdk. For logging into facebook, they provide their own custom made view called 'FBLoginView' which has been made as a subclass of UIView. I just have to allocate memory and add this view to my own view. Then clicking on the view will call all functions provided in the sdk, open the fb app or safari and ask for login and password etc. and then come back to the app.
Now I want to put this button in a UIActionSheet. In the sheet I don't see an option for customizing its buttons. Even if there was an option, how can I use the UIView to create UIButton?
It's not a good idea to mess around with UIActionSheet.
From UIActionSheet Class Reference:
Subclassing Notes
UIActionSheet is not designed to be subclassed, nor should you add views to its hierarchy. If you need to present a sheet with more customization than provided by the UIActionSheet API, you can create your own and present it modally with presentViewController:animated:completion:.
There are a quite a few action sheet replacements. Look at https://www.cocoacontrols.com/search?q=uiactionsheet for a starting list.
My suggestion is to find one with the features you like instead of hacking UIActionSheet.
I found this one from another user on StackOverflow, you might find it useful, as I did
Add UIView as subView on UIActionSheet
Good luck and let us know if you find any other solutions :)
UPDATE: I'd stay away from UIACtionSheet, as it's deprecated in iOS 8 onward. You need to use UIAlertController now. I'd use something like this:
UIAlertController * alert=[UIAlertController alertControllerWithTitle:#"Connection Method"
message:#"Select Connection Method"
preferredStyle:UIAlertControllerStyleActionSheet];
//These will essentially be the buttons that we used to create for UIActionSheet. They are created pretty much the same way as how they used to,
//but you have to declare their corresponding action here too.
UIAlertAction* firstAction = [UIAlertAction actionWithTitle:#"Title1"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
declaredVariable = NO;
[alert dismissViewControllerAnimated:NO completion:nil];
[self doSomethingHereOrExecuteTheFunctionYouNeed];
}];
UIAlertAction* secondAction = [UIAlertAction actionWithTitle:#"Title2 and so on"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
BOOL newVarable = YES;
[alert dismissViewControllerAnimated:NO completion:nil];
[self followAlertControllerChoice];
}];
UIAlertAction* cancel = [UIAlertAction actionWithTitle:#"Cancel button"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
[alert dismissViewControllerAnimated:YES completion:nil];
}];
//Add buttons ("Actions") here
[alert addAction:firstAction];
[alert addAction:secondAction];
[alert addAction:cancel];
//Finally, present your alertcontroller
[self presentViewController:alert animated:YES completion:nil];
AFAIK, you can only add textfields to the alertcontroller, and you can do it like this:
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.placeholder = #"Enter your text here";
textField.textAlignment = NSTextAlignmentLeft;
//So On so forth
}];
Apparently, Apple doesn't want us to subclass the UIAlertController, per here:
The UIAlertController class is intended to be used as-is and does not
support subclassing. The view hierarchy for this class is private and
must not be modified.

Resources