iOS 8.3版本的iPad上,UIAlertView消失后键盘弹出的问题

34

随着最新的iOS 8.3发布,我们的应用程序开始出现奇怪的行为。

在完成文本字段编辑后,用户可以点击关闭按钮,这将弹出一个 UIAlertView。当用户在警报视图中点击放弃时,警报视图和当前视图都会被解除。但是,在视图消失后键盘会弹出来,这对用户来说非常烦人。

经过一些调试,似乎键盘是显示在用户关闭视图之前访问的最后一个文本字段上。我尝试了各种方法,在许多地方(在显示UIAlertView之前,在UIAlertView中单击按钮后;我甚至将焦点设置为视图的另一个UI元素)中结束当前视图的endEditing。这并没有解决问题。

但是对于这个特定问题,我不确定它是否是一个普遍的问题,还是我们需要修复它。在iOS 8.3之前,一切都运作得非常完美。

我们知道UIAlertView已经在iOS 8中被弃用。我们正在开始迁移到UIAlertController。但是,如果有任何变通方法,我们很乐意听取建议。

这里是一些代码片段。

- (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];
}

在显示警告视图之前,您是否尝试在文本字段上调用resignFirstResponder? - sixthcent
是的,我为所有文本字段调用了resignFirstResponder方法,并对当前视图调用了endEditing方法。但似乎没有作用 :( - Helen
您可能需要向我们展示代码以便进一步帮助。看起来警告视图的解除正在触发您的视图控制器上的某些生命周期调用。 - sixthcent
我在原始帖子中添加了一段代码片段。谢谢。 - Helen
2
可能是重复的问题:如果我使用UIAlertView,键盘会失去隐藏能力 - Marcus Adams
显示剩余2条评论
7个回答

14
如果您的部署目标是iOS 8+,请尝试使用UIAlertController。 这里是UIAlertView的快速修复方法:在文本字段或文本视图取消第一响应者时延迟显示警报视图的调用。
[self performSelector:@selector(showAlertView) withObject:nil afterDelay:0.6];

对我来说可行。你选择使用0.6秒的延迟有什么原因吗? - Keith Aylwin
@KeithAylwin 我随机选择了这个数字。你可以自己选择另一个数字 :) - Yiming Tang

3
如果有人遇到困难,希望这能帮到你:
if (NSClassFromString(@"UIAlertController")) {
    UIAlertController* alert = ...
}
else {
    UIAlertView* alert = ...
}

这种方式绝对不是检查 UIAlertController 是否可用的正确方法。你应该使用 if (NSClassFromString(@"UIAlertController")) { 这样的方式,如果类不存在,则返回 nil。如果使用上述方式,你的应用程序将在 iOS 7.1 或更早版本上崩溃。 - Rich
当你给答案点踩时,请至少解释一下原因。仅仅按下踩并不能帮助看到答案的问题,或者至少在编辑后剩下的部分中有什么问题。 - puzzler
我不同意在你编辑后对这个回答进行负评(这是针对@pkamb而不是@puzzler的)。我之前对这个回答下了负评,但由于它已经被编辑,我撤销了这个投票。如果我在审核队列中审查该编辑,我也不确定是否会接受它。 - Rich
似乎之前的回答是“使用这段代码 编辑: 等等,改用这段代码”。我删除了读者被告知忽略的前面部分。如果我删掉太多,请随意补充当前的答案,并接受我的道歉。 - pkamb
1
所以,实际上现在的答案是正确的。这就是我需要知道的。谢谢你们两个的解释。 - puzzler

2

您需要更改iOS 8.3的警告提示。

首先,在您的视图中添加以下内容:

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

那么

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];

这将帮助你解决与我相同的问题。 上面的代码适用于iOS 7和8。


1
最佳实践是使用类/respondsToSelector:检查,而不是使用iOS版本号:https://dev59.com/pF8f5IYBdhLWcg3wB-3E - pkamb
不要自己调用 -[dismissViewControllerAnimated:completion:] 来关闭。UIAlertViewController 会在调用处理程序块之前或之后自行关闭。同时,直接访问 alertVC 将导致 actionalertController 之间的保留循环。别问我为什么知道这个... - Veight Zhou

2

我也遇到过在关闭UIAlertController后键盘会弹出(光标在上次使用的textView中),这里有一个非常简单的解决方法:

在构建和呈现UIAlertController之前,

使用[_activeTextView resignFirstResponder]; 键盘将重新出现。 使用[self.view endEditing:YES]; 键盘将不会重新出现。

希望这可以帮助你。


这个解决方案适用于Swift 4。只是为了明确起见:self.view.endEditing(true)。 - user165242

1
如果一个文本字段是第一响应者,当警告框被解除时,它将自动弹出键盘。请确保第一响应者被正确解除:

[textField resignFirstResponder]

请记住:在表格或滚动视图中,有时必须将字段显示在屏幕上才能正确地取消响应者。

如果没有第一响应者处于活动状态,则在警报被解除时不应出现键盘。

对于此问题中的特定情况,我建议设置一个委托方法来侦听“完成”按钮并在委托回调中放弃第一响应者。

或者,在开始编辑时,您可以存储对当前活动文本字段的引用,然后在“clickedButtonAtIndex”方法中,如果仍处于活动状态,则可以放弃活动文本字段。


请问您能否提供一个正确的示例?虽然我没有在滚动视图中,但键盘似乎经常弹出。 - meteors
[textField resignFirstResponder]:[textField resignFirstResponder] - Lytic

1

尝试使用以下代码。它适用于iOS 8及以下版本。

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];
    }

}


0

我注意到textField键盘和alertView的一些奇怪行为...也许可以创建一个名为disableKeyboard的布尔变量,并像这样使用它:

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

    if (disableKeyBoard) {

        disableKeyboard = NO;
        return NO;

    } else {

        return YES;

    }

}

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

    disableKeyboard = YES;

}

这只是一个权宜之计,无法解决核心问题,无论它是什么。为了使此方法工作,您需要在标头中设置alertView和textField delegate方法。


textFieldShouldBeginEditing 不触发键盘闪烁事件。 - Konstantin Salavatov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接