如何同时关闭 UIAlertController 和键盘?

15

我使用UIAlertController创建了一个注册表单,并使用addTextFieldWithConfigurationHandler方法添加了一个文本框。但是出现了一个小问题。

当表单出现时,键盘和模态视图以平滑的动画显示。在关闭表单时,模态视图先消失,然后键盘才消失。这使得键盘出现了突然向下的落差。

我如何让模态视图和键盘优雅地消失?

lazy var alertController: UIAlertController = { [weak self] in
    let alert = UIAlertController(title: "Alert", message: "This is a demo alert", preferredStyle: .Alert)
    alert.addTextFieldWithConfigurationHandler { textField in
        textField.delegate = self
    }
    alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
    return alert
}()

@IBAction func alert() {
    presentViewController(alertController, animated: true, completion: nil)
}

func textFieldShouldReturn(textField: UITextField) -> Bool {
    alertController.dismissViewControllerAnimated(true, completion: nil)
    return true
}

呈现和解除


请您能否发布您用来创建UIAlertController和actions的代码。谢谢。 - Jim Tierney
如果您能分享一个测试应用程序,我很愿意为您检查它。 - Shai
8个回答

15

您可以将视图控制器或其他对象设置为 UIAlertController(alert.transitioningDelegate)的过渡代理,并为其进行自定义动画以进行解除。 代码示例:

@interface ViewController () <UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning, UITextFieldDelegate>
@property (assign, nonatomic) NSTimeInterval keyboardAnimationDuration;
@property (assign, nonatomic) CGFloat keyboardHeight;
@property (nonatomic, strong) UIAlertController *alertController;
@property (nonatomic,strong) id <UIViewControllerTransitioningDelegate> transitioningDelegateForAlertController;
@end

@implementation ViewController

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self subscribeForKeyboardNotification];
}

#pragma mark - Keyboard notifications

- (void)subscribeForKeyboardNotification {
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillAppear:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];
}

- (void)keyboardWillAppear:(NSNotification *)notification {
    self.keyboardAnimationDuration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    self.keyboardHeight = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height;
}

#pragma mark - IBAction

- (IBAction)showAlertButtonPressed:(id)sender {
    [self showAlert];
}

- (void)showAlert {
    self.alertController = [UIAlertController alertControllerWithTitle:@"Alert"
                                                                             message:@"This is a demo alert"
                                                                      preferredStyle:UIAlertControllerStyleAlert];
    __weak typeof(self) weakSelf = self;
    [self.alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
        textField.delegate = weakSelf;
    }];
    self.transitioningDelegateForAlertController = self.alertController.transitioningDelegate;
    self.alertController.transitioningDelegate = self;
    [self.alertController addAction:[UIAlertAction actionWithTitle:@"Ok"
                                                        style:UIAlertActionStyleCancel
                                                      handler:nil]];
    [self presentViewController:self.alertController animated:YES completion:nil];
}

#pragma mark - UITextFieldDelegate

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [self.alertController dismissViewControllerAnimated:YES completion:nil];
    return YES;
}

#pragma mark - UIViewControllerTransitioningDelegate

- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented
                                                                   presentingController:(UIViewController *)presenting
                                                                       sourceController:(UIViewController *)source {
    return [self.transitioningDelegateForAlertController animationControllerForPresentedController:presented
                                                                          presentingController:presenting
                                                                              sourceController:source];
}

- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
    return self;
}

#pragma mark - UIViewControllerAnimatedTransitioning

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
    return self.keyboardAnimationDuration ?: 0.5;
}

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
    UIViewController *destination = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    if ([destination isBeingPresented])
        [self animatePresentation:transitionContext];
    else
        [self animateDismissal:transitionContext];
}

- (void)animatePresentation:(id <UIViewControllerContextTransitioning>)transitionContext {
    NSTimeInterval transitionDuration = [self transitionDuration:transitionContext];
    UIViewController *fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView *container = transitionContext.containerView;
    fromController.view.frame = container.bounds;
    toController.view.frame = container.bounds;
    toController.view.alpha = 0.0f;
    [container addSubview:toController.view];
    [fromController beginAppearanceTransition:NO animated:YES];
    [UIView animateWithDuration:transitionDuration
                     animations:^{
                         toController.view.alpha = 1.0;
                     }
                     completion:^(BOOL finished) {
                         [fromController endAppearanceTransition];
                         [transitionContext completeTransition:YES];
                     }];
}

- (void)animateDismissal:(id <UIViewControllerContextTransitioning>)transitionContext {
    NSTimeInterval transitionDuration = [self transitionDuration:transitionContext];
    UIViewController *fromController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    [toController beginAppearanceTransition:YES animated:YES];
    [UIView animateWithDuration:transitionDuration
                     animations:^{
                         fromController.view.alpha = 0.0;
                         [fromController.view endEditing:YES];
                         CGRect frame = fromController.view.frame;
                         frame.origin.y += self.keyboardHeight / 2;
                         fromController.view.frame = frame;
                     }
                     completion:^(BOOL finished) {
                         [toController endAppearanceTransition];
                         [transitionContext completeTransition:YES];
                     }];
}

@end

结果:

enter image description here

P.S.: 我使用了旧的alert过渡代理进行演示,因为我无法复制原始动画。因此,animatePresentation:方法从未被使用。


非常有教育意义,谢谢。您能否发布一下您的想法的示例代码? - Thons
请尝试我的答案中的代码,并告诉我您期望的动画类型。 - Vlad
我期待着当警告消失时,键盘同时消失的动画效果,就像它们一起出现一样。 - Thons

9

我有和你一样的问题,后来无意中找到了解决方法。也许你已经不需要了,但是为了像我一样的其他人,请看下面的答案:

Swift:

(斜体)
override func canBecomeFirstResponder() -> Bool {
    return true
}

Objective-C:

- (BOOL)canBecomeFirstResponder {
    return true;
}

只需将此代码添加到处理警报的视图控制器中。仅在Swift中进行过测试。


2
很简单。如果你的UIAlertController委托存在于self视图控制器中,则可以在其委托方法中进行操作,用于关闭AlertController。您可以在UIAlertController对象中执行[youtTextField resignFirstResponder],该对象具有关闭它的按钮(如OK或Cancel),以便关闭已呈现的键盘。我没有尝试过,但这应该可行。但是,您必须正确处理textField和Alert。

1
抱歉,我不理解这个。你能提供一小段代码吗? - Randomblue
在iOS中,当你写[textfield resignFirstResponder]时,它会从第一响应者中辞职并隐藏键盘。因此,在alertdelegate方法中,您可以编写[textfield resignFirstResponder],这样键盘就会隐藏,警报也会被解除。 - Bhoomi Jagani

1
我假设 UIAlertController 跳出的原因是在键盘上按下“返回”后它被解散了。如果是这样,我已经找到了一种方法让警报和键盘从返回操作中平滑地解散。
您需要在类文件中声明 UIAlertController。
@property (strong, nonatomic) UIAlertController *alertController;

你还需要在viewController中使用UITextFieldDelegate。当将textField添加到UIAlertController时,你需要将其委托设置为self。(由于它在块内部使用,因此使用weakSelf)

@interface ViewController ()<UITextFieldDelegate>

在这个方法中,您正在拍卖UIAlertController -

   self.alertController = [UIAlertController alertControllerWithTitle:@"Alert" message:@"This is the message" preferredStyle:UIAlertControllerStyleAlert];


   __weak typeof(self) weakSelf = self;

   [self.alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
       textField.delegate = weakSelf;
   }];

   [self presentViewController:self.alertController animated:YES completion:nil];

添加这个UITextField代理方法,它将在键盘上的返回按钮被按下后触发。这意味着您可以在键盘消失之前处理UIAlertController以进行解散操作,从而使其顺利运行。

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

   [self.alertController dismissViewControllerAnimated:YES completion:nil];

   return YES;

}

我已经测试过了,应该能够完全按照你的要求工作。
谢谢, 吉姆

谢谢你,@Jim Tierney。我使用了你的代码重新编辑了问题,并且结果显示在截图的后半部分。 - Thons

0
   - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex

   { 
       [self.view endEditing:YES];
      // or you can write [yourtextfield refignFirstResponder]
       [alertView dismissWithClickedButtonIndex:buttonIndex animated:TRUE];
   }

1
你所提到的这个委托方法是为UIAlertview设计的,而UIAlertController不使用它,因为它的解除将在UIAlertController使用的UIAction块内处理。 - Jim Tierney

0

为UIAlertController的viewWillDisappear方法编写Swizzle,对应的文本字段执行resignFirstResponder或在控制器的视图上调用endEditing:

我正在使用ReactiveCocoa进行此操作:

        let alert = UIAlertController(title: "", message: "", preferredStyle: .Alert)

        alert.addTextFieldWithConfigurationHandler {
           textField in
        }

        let textField = alert.textFields!.first!

        alert.rac_signalForSelector(#selector(viewWillDisappear(_:)))
             .subscribeNext {
                 _ in
                 textField.resignFirstResponder()
             }

0

不需要做任何事情,你只需要实现这段代码,它对我来说很有效,无需声明任何类型的 委托方法

- (void)showAlert {
self.alertController = [UIAlertController alertControllerWithTitle:@"Alert"
                                                           message:@"Enter Name:"
                                                    preferredStyle:UIAlertControllerStyleAlert];
[self.alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {

}];
[self.alertController addAction:[UIAlertAction actionWithTitle:@"Ok"
                                                         style:UIAlertActionStyleCancel
                                                       handler:nil]];
[self presentViewController:self.alertController animated:YES completion:nil];

}


0
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex

   { 

if (buttonIndex==1) {

        [[alertView textFieldAtIndex:0] resignFirstResponder];

        } else {

            [[alertView textFieldAtIndex:0] resignFirstResponder];

        }
    }

使用您的按钮索引(确定或取消按钮索引)


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