15

Preamble

So I have an application featuring a chat section, and I'm synchronizing the animation of the keyboard hiding and showing with the rise and fall of the chat input.

Here's the code I'm using:

SHOW:

- (void) keyboardWillShow:(NSNotification *)note {

    NSDictionary *keyboardAnimationDetail = [note userInfo];
    UIViewAnimationCurve animationCurve = [keyboardAnimationDetail[UIKeyboardAnimationCurveUserInfoKey] integerValue];
    CGFloat duration = [keyboardAnimationDetail[UIKeyboardAnimationDurationUserInfoKey] floatValue];

    NSValue* keyboardFrameBegin = [keyboardAnimationDetail valueForKey:UIKeyboardFrameBeginUserInfoKey];
    CGRect keyboardFrameBeginRect = [keyboardFrameBegin CGRectValue];

    // working for hardware keyboard
    //UIViewAnimationOptions options = (UIViewAnimationOptions)animationCurve;

    // working for virtual keyboard
    UIViewAnimationOptions options = (animationCurve << 16);

    [UIView animateWithDuration:duration delay:0.0 options:options animations:^{
        textView.frame = CGRectMake(0, self.view.bounds.size.height - keyboardFrameBeginRect.size.height, self.view.bounds.size.width, -40);
    } completion:nil];

}

HIDE:

- (void) keyboardWillHide:(NSNotification *)note {

    NSDictionary *keyboardAnimationDetail = [note userInfo];
    UIViewAnimationCurve animationCurve = [keyboardAnimationDetail[UIKeyboardAnimationCurveUserInfoKey] integerValue];
    CGFloat duration = [keyboardAnimationDetail[UIKeyboardAnimationDurationUserInfoKey] floatValue];

    // hardware keyboard
    //UIViewAnimationOptions options = (UIViewAnimationOptions)animationCurve;

    // virtual keyboard
    UIViewAnimationOptions options = (animationCurve << 16);

    [UIView animateWithDuration:duration delay:0.0 options:options animations:^{
        textView.frame = CGRectMake(0, self.view.bounds.size.height, self.view.bounds.size.width, -40);
    } completion:nil];

}

This works great with the virtual keyboard, but if keyboardWillShow: or keyboardWillHide: is called as a result of disconnecting or connecting a hardware keyboard, the animation lags. I can fix this by changing the UIViewAnimationOptions

Replace:

// Works with virtual keyboard
UIViewAnimationOptions options = (animationCurve << 16);

With:

// working for firstResponder keyboard
UIViewAnimationOptions options = (UIViewAnimationOptions)animationCurve;

But with this, now the virtualKeyboard animation lags

I realize that hardware keyboard animations aren't very common and it's perhaps not the most important issue, but I like everything to just work!

Examples

VirtualKeyboard w/ (animationCurve << 16) -- WORKING

VirtualKeyboard w/ (animationCurve << 16)

VirtualKeyboard w/ (UIViewAnimationOptions)animationCurve -- BROKEN

VirtualKeyboard w/ (UIViewAnimationOptions)animationCurve

HardwareKeyboard w/ (animationCurve << 16) -- BROKEN

HardwareKeyboard w/ (animationCurve << 16)

HardwareKeyboard w/ (UIViewAnimationOptions)animationCurve -- WORKING

HardwareKeyboard w/ (UIViewAnimationOptions)animationCurve

Notes

To simulate hardware keyboard in simulator cmd + shft + k

Yes, this is replicable on real device.

In case you want it, here's the rest of my code, just for replication purposes

ADD TEXT VIEW

textView = [UITextView new];
textView.layer.borderWidth = 10;
textView.layer.borderColor = [UIColor blackColor].CGColor;
textView.frame = CGRectMake(0, self.view.bounds.size.height, self.view.bounds.size.width, -40);
[self.view addSubview:textView];

UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]init];
[tap addTarget:self action:@selector(handleTap:)];
[self.view addGestureRecognizer:tap];

// OBSERVE KEYBOARD
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillShow:)
                                             name:UIKeyboardWillShowNotification
                                           object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillHide:)
                                             name:UIKeyboardWillHideNotification
                                           object:nil];

HANDLE TAP:

- (void) handleTap:(UITapGestureRecognizer *)tap {
    NSLog(@"tapped");
    [textView resignFirstResponder];
}

The Question:

What is going on here, and is there a good way to get consistent animation regardless of virtual / hardware keyboard?

I realize this is long, thank you for reading!

Logan
  • 52,262
  • 20
  • 99
  • 128
  • Your question is very long and hard to understand dude. Try removing the unnecessary parts to increase the readability. – Rafał Sroka Mar 07 '14 at 21:16
  • 3
    What would you suggest I cut? I want to provide all of the necessary code and explanation for people to replicate the problem, and I included the visuals so people could see what I'm talking about. I understand it's long, but I think its mostly relevant information. – Logan Mar 07 '14 at 21:17
  • Same problem here, if you find your solution then please post it here. – Anand Suthar Apr 21 '14 at 10:58
  • You might solve it by listening for hardware keyboard connection (depends on the timings, i didn't check): http://stackoverflow.com/a/2893327/653513 - and use appropriate animation according to the type of keyboard. It's a private API though - so it could serve just as proof-of-concept. – Rok Jarc Apr 25 '14 at 17:32
  • @Logan: How did you capture those nifty animated gifts? – Ricardo Sanchez-Saez Jul 24 '14 at 17:33
  • 1
    @RicardoSánchez-Sáez - I use licecap, it's pretty great! http://www.cockos.com/licecap/ – Logan Jul 24 '14 at 17:36
  • did you find the solution? – Berk Kaya Jan 19 '18 at 14:14
  • @Logan looks like the problem is with the way you specify animation curve: correct way to do is ```UIViewAnimationOptions options = animationCurve;```, and if you specify some options together - use binary OR: ```options = op1 | op2 | op3;``` – Oleg Shanyuk Jan 23 '18 at 11:13
  • @OlegShanyuk thanks for commenting, but that's not really related to the underlying curve issue here and how that specifically pertains to digital/hardware keyboard animation. – Logan Jan 24 '18 at 09:56

2 Answers2

3

Since the animation curve Apple sends you in the keyboard notification does not have a corresponding UIViewAnimationOption bit, you need to drop down to old-school non-block animations and use the curve directly:

NSTimeInterval duration = [note.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationCurve curve = [note.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
[UIView beginAnimations:@"SomeAnimationID" context:NULL];
[UIView setAnimationCurve:curve];
[UIView setAnimationDuration:duration];
// Animation code
[UIView commitAnimations];
Adlai Holler
  • 830
  • 7
  • 10
  • Thanks for the answer Adlai, I've got a couple things to work on at the moment, but I'll try this later today and let you know if it solved the problem. – Logan May 13 '14 at 17:31
  • Hi @Adlai, thanks for the answer. I was unable to get this to work. Have you tested it, or was it just an idea? – Logan May 14 '14 at 21:29
  • I've tested it and it works well for me. What animation code are you using? – Adlai Holler May 14 '14 at 22:51
  • Exactly what's in the question. I replaced the animation blocks w your code. – Logan May 14 '14 at 23:05
  • Replace my `// Animation code` comment with your `textView.frame = …` code – Adlai Holler May 15 '14 at 03:04
  • I did exactly that, unfortunately, I'm still getting the same problem. – Logan May 19 '14 at 17:55
  • I also do have the same problem and the here presented solution also doesn't work for me. Any news on that? – Kai Dec 20 '16 at 15:05
-1

keyboardWillShow:

NSDictionary *info = [notification userInfo];
CGRect keyboardFrame = [info[UIKeyboardFrameEndUserInfoKey] CGRectValue];
NSTimeInterval duration = [info[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
UIViewAnimationCurve curve = [info[UIKeyboardAnimationCurveUserInfoKey] integerValue];

[self layoutIfNeeded];
[UIView animateWithDuration:duration delay:0 options:(curve << 20) animations:^{
    [self.keyboardConstraint setConstant:keyboardFrame.size.height];
    [self layoutIfNeeded];
} completion:nil];
Genja Grishin
  • 122
  • 2
  • 6