Как уволить UIAlertController и клавиатуру одновременно?

Я создал регистрационную форму с 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
}

presenting and dismissing

Ответы

Ответ 1

Вы можете установить контроллер просмотра или другой объект в качестве передающего делегата вашего 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.: Я использовал старый передающий делегат для презентации, потому что я не могу воспроизвести оригинальную анимацию. Поэтому метод animatePresentation: никогда не используется.

Ответ 2

У меня была та же самая проблема, что у вас была, и нашел решение случайно. Вероятно, вам это больше не нужно, но ради других, подобных мне, вот ответ:

Swift:

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

Objective-C:

- (BOOL)canBecomeFirstResponder {
    return true;
}

Просто добавьте этот код в контроллер просмотра, обрабатывающий предупреждение. Проверено только быстро.

Ответ 3

Его довольно просто.

если ваш делегат UIAlertController присутствует в self View Controller. то вы можете сделать это в своем методе делегирования для Dismiss AlertController. Вы можете [youtTextField resignFirstResponder] в вашем объекте UIAlertController, у которого есть кнопка для его удаления. (например, ОК или Отмена), поэтому ваш представленный KeyBoard будет отклонен.

Я не пробовал, но это сработает. но вы должны правильно обрабатывать textField и Alert.

Ответ 4

Я предполагаю, что спуск UIAlertController происходит, если он отклоняется после нажатия "return" на клавиатуре. Если это так, я нашел способ для Alert и клавиатуры плавно отклоняться от действия возврата.

Вам потребуется объявить UIAlertController в файле класса

@property (strong, nonatomic) UIAlertController *alertController;

И вам также нужно будет использовать UITextFieldDelegate с viewController Добавляя textField к UIAlertController, вам нужно будет установить его делегат. (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;

}

Я тестировал это и должен работать точно так, как вам нужно.

Спасибо, Джим

Ответ 5

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

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

Ответ 6

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

   { 

if (buttonIndex==1) {

        [[alertView textFieldAtIndex:0] resignFirstResponder];

        } else {

            [[alertView textFieldAtIndex:0] resignFirstResponder];

        }
    }

Используйте индекс вашей кнопки (Ok или индекс кнопки "Отмена" )

Ответ 7

Вам не нужно ничего делать, чтобы просто реализовать эту часть кода, это работает для меня, не нужно объявлять какие-либо методы делегата

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

}

Ответ 8

Swizzle viewWillDisappear метод для UIAlertController и выполнить 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()
             }