Paul Warkentin
Paul Warkentin

Reputation: 3899

iOS 7 - Keyboard animation

I'm trying to understand the new keyboard animation in iOS 7.0 on the iPhone 5 Simulator. I want to resize my UITableView when the keyboard appears, but I can't get the right animation details.
I'm using the information from the NSNotification object, when the keyboard appears or disappears.

Here is my log:

Move keyboard from {{0, 920}, {320, 216}} to {{0, 352}, {320, 216}}
 with duration: 0.400000
 and animation curve: 7

UIViewAnimationCurveEaseInOut = 0
UIViewAnimationCurveEaseIn = 1
UIViewAnimationCurveEaseOut = 2
UIViewAnimationCurveLinear = 3

The animation curve is an unknown value, what should I do?

Upvotes: 48

Views: 33339

Answers (8)

E. Rivera
E. Rivera

Reputation: 10938

In iOS 13 you are in an implicit animation, so just modify your layout inside your notification handler and it will animate with the correct duration and curve.

Upvotes: 1

Anu Mittal
Anu Mittal

Reputation: 301

In Swift 4


Add observers for keyboard notifications:


  • UIKeyboardDidShowNotification
  • UIKeyboardDidHideNotification

via

NSNotificationCenter.defaultCenter().addObserver(_ observer: Any, 
        selector aSelector: Selector, 
        name aName: NSNotification.Name?, 
        object anObject: Any?)

And implement selector to animate the UI with Keyboard animation. In order to create a valid curve value we need to shift UIResponder.keyboardAnimationCurveUserInfoKey by << 16


func keyboardWillShow(_ notification: Notification!) {
    if let info = notification.userInfo {
        let keyboardSize = info[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
        let duration = info[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double 
        let curveVal = (info[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber)?.intValue ?? 7 // default value for keyboard animation
        let options = UIView.AnimationOptions(rawValue: UInt(curveVal << 16))
        UIView.animate(withDuration: duration, delay: 0, options: options, animations: {
        // any operation to be performed
    }, completion: nil)
    }
}

Upvotes: 2

Anton Gaenko
Anton Gaenko

Reputation: 9015

You can use animateWithDuration block and set curve inside it. It's clean and work well.

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

Happy coding!

UPDATE

You can use a simple UIViewController category written by me https://github.com/Just-/UIViewController-KeyboardAnimation

Upvotes: 12

Jeffrey Sun
Jeffrey Sun

Reputation: 8049

Use UIKeyboardWillChangeFrameNotification instead, because some international keyboards, like the Chinese keyboard, change height during use. Also this code gives you the correct heights for the keyboard, even in landscape mode. (Note: the code below is for Autolayout)

//set your observer, in a method like viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChange:) name:UIKeyboardWillChangeFrameNotification object:nil];

- (void)keyboardWillChange:(NSNotification *)notification {
    CGRect initialRect = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
    CGFloat initialHeight = self.view.frame.size.height - [self.view convertRect:initialRect fromView:nil].origin.y;
    CGRect keyboardRect = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
    CGFloat newHeight = self.view.frame.size.height - [self.view convertRect:keyboardRect fromView:nil].origin.y;
    //set your constraints here, based on initialHeight and newHeight, which are the heights of the keyboard before & after animation.
    [self.contentView setNeedsUpdateConstraints];
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:[notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.contentView layoutIfNeeded];
    [UIView commitAnimations];
}

Upvotes: 5

invoodoo
invoodoo

Reputation: 3906

To use the same animation as keyboard has, you have to use undocumented Curve option.

- (void)keyboardWillHide:(NSNotification *)notification {
    NSDictionary *userInfo = [notification userInfo];

    CGRect rect = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
    NSTimeInterval animationDuration = [[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    NSInteger curve = [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue] << 16;

    [UIView animateWithDuration:animationDuration delay:0.0 options:curve animations:^{

    } completion:nil];
}

I found that actually method animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion: is the new way that all animations are done in iOS7 and iOS8 now. You just make right duration, damping and velocity and you will get the same effect, but even you can change speed/time.

Upvotes: 4

Paul Warkentin
Paul Warkentin

Reputation: 3899

Now I found the solution. The animation starts from the point {0, 920} to {0, 352}. The problem was that the UITableView object started with a size of {160, 568}, so I changed the size of the UITableView to {160, 920} before the animation was started.

Concerning to the unknown animation curve, I just set the parameter to animationCurve << 16 to convert it from a view animation curve to a view animation option.
The value is not equal to the linear, ease in, ease out and ease inout animation curve.

Here is my code:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(_keyboardWillShow:)
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

and:

- (void)keyboardWillShow:(NSNotification *)aNotification {
    NSDictionary *userInfo = aNotification.userInfo;

    //
    // Get keyboard size.

    NSValue *beginFrameValue = userInfo[UIKeyboardFrameBeginUserInfoKey];
    CGRect keyboardBeginFrame = [self.view convertRect:beginFrameValue.CGRectValue fromView:nil];

    NSValue *endFrameValue = userInfo[UIKeyboardFrameEndUserInfoKey];
    CGRect keyboardEndFrame = [self.view convertRect:endFrameValue.CGRectValue fromView:nil];

    //
    // Get keyboard animation.

    NSNumber *durationValue = userInfo[UIKeyboardAnimationDurationUserInfoKey];
    NSTimeInterval animationDuration = durationValue.doubleValue;

    NSNumber *curveValue = userInfo[UIKeyboardAnimationCurveUserInfoKey];
    UIViewAnimationCurve animationCurve = curveValue.intValue;

    //
    // Create animation.

    CGRect tableViewFrame = self.tableView.frame;
    bTableViewFrame.size.height = (keyboardBeginFrame.origin.y - tableViewFrame.origin.y);
    self.tableView.frame = tableViewFrame;

    void (^animations)() = ^() {
        CGRect tableViewFrame = self.tableView.frame;
        tableViewFrame.size.height = (keyboardEndFrame.origin.y - tableViewFrame.origin.y);
        self.tableView.frame = tableViewFrame;
    };

    //
    // Begin animation.

    [UIView animateWithDuration:animationDuration
                          delay:0.0
                        options:(animationCurve << 16)
                     animations:animations
                     completion:nil];
}

Upvotes: 47

Michael Peterson
Michael Peterson

Reputation: 10532

Register for the notification:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];

Respond by animating a change to the frame.origin.y of the view.

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

    NSDictionary *userInfo = aNotification.userInfo;
    NSValue *endFrameValue = userInfo[UIKeyboardFrameEndUserInfoKey];
    CGRect keyboardEndFrame = [self.view convertRect:endFrameValue.CGRectValue fromView:nil];

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

    CGRect searchButtonFrame = self.searchButton.frame;
    searchButtonFrame.origin.y = (keyboardEndFrame.origin.y - searchButtonFrame.size.height);
    self.searchButton.frame = searchButtonFrame;

    [UIView commitAnimations];
}

Upvotes: 0

David Beck
David Beck

Reputation: 10159

In iOS 7, the keyboard uses a new, undocumented animation curve. While some have noted that using an undocumented value for the animation option, I prefer to use the following:

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

// work

[UIView commitAnimations];

While block based animations are the recommendation, the animation curve returned from the keyboard notification is an UIViewAnimationCurve, while the option you would need to pass to block based animations is an UIViewAnimationOptions. Using the traditional UIView animation methods allows you to pipe the value directly in. Most importantly, this will use the new undocumented animation curve (integer value of 7) and cause the animation to match the keyboard. And, it will work just as well on iOS 6 and 7.

Upvotes: 72

Related Questions