34

With the latest iOS 8.3 release, our app starts to have a weird behavior.

After finishing textfield editing, the user can click the close button which brings up an UIAlertView. When the user clicks discard in the alertview, alertview and current view are dismissed. But somehow the keyboard shows up after the view is gone which is quite annoying to users.

After some debugging, it seems that the keyboard is shown for the last textfield that the user has accessed before closing the view. I tried various ways to endEditing for the current view in many places (before showing UIAlertView, after clicking a button in the UIAlertView; I even set the focus to another UI element of the view). It didn't solve the problem.

But for this particular issue, I'm not sure if it's a common issue or something we need to fix. Everything works perfectly before iOS 8.3.

We understand that UIAlertView is deprecated for iOS 8. We're starting to migrate to UIAlertController. But if there's any workaround, we'd love to hear.

Here's some code snippet.

- (IBAction)closeTapped:(UIButton *)sender
{
    // try to resign first responder
    // [self.tfName resignFirstResponder];
    // [self.tfPosition resignFirstResponder];
    [self.view endEditing:YES];

    if(self.orderDetails.isOpen && self.orderItemChanged)
    {
        UIAlertView* saveAlert = [[UIAlertView alloc] initWithTitle:@"Unsaved Changes"
                                                            message:@"Your changes have not been saved. Discard changes?"
                                                           delegate:self
                                                  cancelButtonTitle:@"Cancel"
                                                  otherButtonTitles:@"Save", @"Discard", nil];
        [saveAlert show];
    }
    else
    {
        [self close];
    }
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    switch(buttonIndex)
    {
        case 1: // Save
        {
            [self save];
            break;
        }
        case 2: // Discard
        {
            [self close];
            break;
        }
    }
}

- (void)close
{   
    [self.delegate dismissEditOrderItemVC];
}
Helen
  • 341
  • 1
  • 3
  • 4
  • Did you try calling resignFirstResponder on the text field before showing the alert view? – sixthcent May 08 '15 at 17:15
  • Yes, I did resignFirstResponder for all textfields and endEditing for current view. Doesn't seem working :( – Helen May 09 '15 at 00:05
  • You may have to show us the code to be able to help further. It appears that the dismissal of alert view is triggering some lifecycle calls on your view controller. – sixthcent May 09 '15 at 14:09
  • I added a code snippet to original post. Thanks. – Helen May 09 '15 at 19:57
  • Are you doing anything in viewWillAppear / viewDidAppear? – sixthcent May 12 '15 at 17:07
  • Nothing. Only in viewDidLoad to set up view structure. – Helen May 13 '15 at 17:27
  • 2
    possible duplicate of [Keyboard loses hiding ability "if I use a UIAlertView"](http://stackoverflow.com/questions/6692319/keyboard-loses-hiding-ability-if-i-use-a-uialertview) – Marcus Adams Aug 10 '15 at 20:59

7 Answers7

14

If your deployment target is iOS 8+, try UIAlertController.

Here's a quick fix for UIAlertView: delay the invocation of showing the alert view when your text field or text view resigns first responder.

[self performSelector:@selector(showAlertView) withObject:nil afterDelay:0.6];
Yiming Tang
  • 557
  • 4
  • 9
3

If anyone struggles with this, I hope this will help:

if (NSClassFromString(@"UIAlertController")) {
    UIAlertController* alert = ...
}
else {
    UIAlertView* alert = ...
}
pkamb
  • 33,281
  • 23
  • 160
  • 191
puzzler
  • 218
  • 3
  • 13
  • This is _not_ the way **at all** to check if `UIAlertController` is available. You should use `if (NSClassFromString(@"UIAlertController")) {` instead which returns `nil` if the class does not exist. Using the above will result in your app crashing on iOS 7.1 or earlier. – Rich Jul 08 '15 at 07:25
  • When you downvote, please at least explain why. Simply pressing down doesn't help seeing what's wrong with the answer, or at least with what's left of it after the edit. – puzzler Aug 17 '15 at 12:23
  • I don't agree with the downvoting of this answer _after_ you've edited (this is aimed at @pkamb, not you @puzzler). The answer in its previous form I downvoted, but as it was edited I removed this vote. I'm not sure I would have accepted that edit either if I'd have reviewed it in the queue either. – Rich Aug 17 '15 at 12:53
  • It seemed like the answer in its previous state said *"use this code **EDIT:** wait use this code instead"*. I edited out the bit at the front that readers were told to disregard. Feel free to add to the current answer if I removed too much, and accept my apologies. – pkamb Aug 17 '15 at 16:55
  • 1
    So actually the answer in it's current form is correct. That was what I needed to know. Thank you both for explaining. – puzzler Aug 18 '15 at 08:44
2

you need to change alert for ios 8.3

first put this in your view

#define IS_IOS8 [[UIDevice currentDevice].systemVersion floatValue] >= 8.0

then

if (IS_IOS8) {

        UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"Unsaved Changes" message:@"Your changes have not been saved. Discard changes?" preferredStyle:UIAlertControllerStyleAlert];

        UIAlertAction *saveAction = [UIAlertAction
                                    actionWithTitle:@"Save"
                                    style:UIAlertActionStyleCancel
                                    handler:^(UIAlertAction *action)
                                    {
                                        [self save];
                                    }];

        UIAlertAction *cancelAction = [UIAlertAction
                                   actionWithTitle:@"Cancel"
                                   style:UIAlertActionStyleCancel
                                   handler:^(UIAlertAction *action)
                                   {
                                       [alertVC dismissViewControllerAnimated:YES completion:nil];
                                   }];


        UIAlertAction *discardAction = [UIAlertAction
                                   actionWithTitle:@"Discard"
                                   style:UIAlertActionStyleCancel
                                   handler:^(UIAlertAction *action)
                                   {
                                       [alertVC dismissViewControllerAnimated:YES completion:nil];
                                   }];



        [alertVC addAction:saveAction];
        [alertVC addAction:cancelAction];
        [alertVC addAction:discardAction];
        [self.view.window.rootViewController presentViewController:alertVC animated:YES completion:nil];

this will help you as it helps me in same problem. above code is compatible with both ios 7 & 8

Nik
  • 1,679
  • 1
  • 20
  • 36
  • 1
    It's best practice to use a class/`respondsToSelector:` check rather than using the iOS version number: http://stackoverflow.com/questions/25111011/uialertview-uialertcontroller-ios-7-and-ios-8-compatibility – pkamb Aug 07 '15 at 18:50
  • DO NOT invoke -[dismissViewControllerAnimated:completion:] to dismiss by yourself. UIAlertViewController will dismiss itself before or after invoking handler block. At the same time, Accessing alertVC directly will cause retain cycle between `action` and `alertController`. Don't ask me why realise it .... – Veight Zhou Oct 26 '15 at 13:54
2

I too, had a keyboard popping up (with the cursor in the last-used textView) after closing a UIAlertController and here is a very simple fix:

Immediately before building and presenting the UIAlertController,

Using [_activeTextView resignFirstResponder]; the keyboard will reappear. Using [self.view endEditing:YES]; the keyboard will NOT reappear.

I hope this helps you.

Steve W.
  • 81
  • 1
  • 7
1

If a text field is the first responder, it will automatically pop up the keyboard when the alert is dismissed. Ensure the first responder is properly dismissed with:

[textField resignFirstResponder]

Remember: in a table or scroll view, sometimes the field must be visible on the screen to properly dismiss the responder.

If there are no first responders active, keyboard should not appear when alert is dismissed.

For the particular case in this question, I would recommend setting a delegate method to listen for the "done" button and resigning the first responder in the delegate callback.

Alternatively, when beginning editing, you can store a reference to the currently active text field, then in your "clickedButtonAtIndex" method you can resign the active text field if it is still active.

Lytic
  • 786
  • 5
  • 12
1

Try using the below code. It works fine for iOS 8 and below version

if (IS_OS_8_OR_LATER) {
        UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];

        UIAlertAction *cancelAction = [UIAlertAction
                                     actionWithTitle:@"OK"
                                     style:UIAlertActionStyleCancel
                                     handler:^(UIAlertAction *action)
                                     {

                                     }];
        [alertVC addAction:cancelAction];

        [[[[[UIApplication sharedApplication] windows] objectAtIndex:0] rootViewController] presentViewController:alertVC animated:YES completion:^{

        }];
    }
    else{
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:msg delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
        [alert show];
    }

}

amitshinik
  • 91
  • 1
  • 10
0

I've noticed some weird behavior with textField keyboards and alertViews as well... Maybe make a bool called disableKeyboard and use it like this:

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {

    if (disableKeyBoard) {

        disableKeyboard = NO;
        return NO;

    } else {

        return YES;

    }

}

- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex {

    disableKeyboard = YES;

}

This is just a workaround and doesn't address the core issue, whatever it is. For this method to work you need to set the alertView and textField delegate methods in your header.

Michael E
  • 656
  • 2
  • 7
  • 13