Как имитировать анимацию клавиатуры на iOS 7, чтобы добавить кнопку "Готово" к цифровой клавиатуре?

Я делал что-то вроде этого, чтобы имитировать анимацию клавиатуры в старой версии iOS.

CGRect keyboardBeginFrame;
[[note.userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] getValue:&keyboardBeginFrame];
self.doneKeyboardButton.frame = CGRectMake(0, (keyboardBeginFrame.origin.y + keyboardBeginFrame.size.height) - 53, 106, 53);
[[[[UIApplication sharedApplication] windows] lastObject] addSubview:self.doneKeyboardButton];

CGPoint newCenter = CGPointMake(self.doneKeyboardButton.superview.frame.origin.x + self.doneKeyboardButton.frame.size.width/2,
                                self.doneKeyboardButton.superview.frame.size.height - self.doneKeyboardButton.frame.size.height/2);

[UIView animateWithDuration:[[note.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]-.02
                      delay:.0
                    options:[[note.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]
                 animations:^{
                     self.contentView.frame = CGRectOffset(self.contentView.frame, 0, -TextFieldViewMovement);
                     self.doneKeyboardButton.center = newCenter;
                 }
                 completion:nil];

Однако это перестало работать на iOS7. Кажется, что возвращаемые значения уже не совсем правильны, а кнопка "Готово" больше не имитирует анимацию отображения клавиатуры.

Ответ 1

В iOS 7 клавиатура использует новую, недокументированную анимационную кривую. Хотя некоторые отметили, что использование недокументированного значения для опции анимации работает, я предпочитаю использовать следующее:

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:[notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue]];
[UIView setAnimationBeginsFromCurrentState:YES];

// work

[UIView commitAnimations];

В то время как анимация на основе блоков является рекомендацией, кривая анимации, возвращаемая с уведомлением о клавиатуре, является UIViewAnimationCurve, тогда как параметр, который вам нужно передать на анимацию на основе блоков, - это UIViewAnimationOptions. Использование традиционных методов анимации UIView позволяет напрямую передавать значение. Самое главное, что это будет использовать новую кривую бездокументальной анимации (целочисленное значение 7) и заставить анимацию соответствовать клавиатуре. И он будет работать так же хорошо на iOS 6 и 7.

Ответ 2

У меня была такая же проблема, и мне удалось заставить анимацию работать со следующими параметрами для iOS 7:

    [UIView animateWithDuration:0.5
                          delay:0
         usingSpringWithDamping:500.0f
          initialSpringVelocity:0.0f
                        options:UIViewAnimationOptionCurveLinear
                     animations:animBlock
                     completion:completionBlock];

EDIT: эти значения были получены с помощью отладки, а может изменяться с новыми версиями iOS. @DavidBeck answer работает для меня в iOS 7, поэтому я связываю его здесь.

Ответ 3

Apple использует некоторую недокументированную анимацию со значением 7 в iOS 7.

Однако объявление UIViewAnimationCurve определяет значения от 0 до 3

typedef enum {
   UIViewAnimationCurveEaseInOut, // 0
   UIViewAnimationCurveEaseIn,
   UIViewAnimationCurveEaseOut,
   UIViewAnimationCurveLinear // 3
} UIViewAnimationCurve;

UIViewAnimationOptions, который вам нужен для анимации на основе блоков, определяется как

enum {
   // ...
   UIViewAnimationOptionCurveEaseInOut            = 0 << 16,
   UIViewAnimationOptionCurveEaseIn               = 1 << 16,
   UIViewAnimationOptionCurveEaseOut              = 2 << 16,
   UIViewAnimationOptionCurveLinear               = 3 << 16,

   UIViewAnimationOptionTransitionNone            = 0 << 20,
   // ...
};

Кажется, что Apple резервирует 4 бита для кривой анимата (20 - 16 = 4), которая позволяет использовать значения от 0 до 15 (поэтому, вероятно, есть больше недокументированных значений).

С помощью этих знаний вы можете просто преобразовать UIViewAnimationCurve в UIViewAnimationOptions, сдвинув его примерно на 16 бит. В вашем примере это означает:

options:[[note.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue] << 16

Ответ 4

Вы можете использовать блок animateWithDuration и задать внутри него кривую. Он чист и работает хорошо.

UIViewAnimationCurve curve = [[notification.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
double duration = [[notification.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];

[UIView animateWithDuration:duration
                    delay:0
                  options:UIViewAnimationOptionBeginFromCurrentState 
               animations:^{
                 [UIView setAnimationCurve:curve];
                 /* ANIMATION HERE */
                 // don't forget layoutIfNeeded if you use autolayout
               }
               completion:nil];

Счастливое кодирование!

UPDATE

Вы можете использовать простую категорию UIViewController, написанную мной https://github.com/Just-/UIViewController-KeyboardAnimation

Ответ 5

Если вы хотите добавить кнопку на панели инструментов (подобно тому, как это делает мобильный Safari), вы можете просто использовать свойство inputAccessoryView (оба UITextField и UITextView его есть) и сэкономить все проблемы, Этот скрытый камень мало известен, я рад, что наткнулся на него.

Он работает как на iOS 6, так и на iOS 7, я использую его в своем приложении Routie.

Ответ 6

Это работает отлично для меня... Продолжительность поймана, когда отображается клавиатура. Я знаю, что жесткое кодирование параметров означает, что это может сломаться в будущем, но оно работает на 7 и 8. Это для нас сейчас достаточно хорошо...

[UIView animateWithDuration:self.animationDuration
                      delay:0.0
                    options:UIViewAnimationOptionCurveEaseInOut
                 animations:^{
                     [self.view layoutIfNeeded]; completion:^(BOOL finished) {
                     if (finished)
                     {
                         if (completion)
                             completion();
                     }
                 }];