Im stuck trying to animate a table view smoothly which has an autolayout contraint. I have a reference to the constraint "keyboardHeight" in my .h and have linked this up in IB. All i want to do is animate the table view with the keyboard when it pops up. Here is my code:
- (void)keyboardWillShow:(NSNotification *)notification
{
NSDictionary *info = [notification userInfo];
NSValue *kbFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
CGRect keyboardFrame = [kbFrame CGRectValue];
CGFloat height = keyboardFrame.size.height;
[UIView animateWithDuration:animationDuration animations:^{
self.keyboardHeight.constant = -height;
[self.view setNeedsLayout];
}];
}
The thing is the animation block is instantaneous and I see white space appear before the keyboard has finished its animation. So basically I see the white background of the view as the keyboard is animating. I cannot make the animation last for as long as the keyboard is animating.
Am i approaching this the wrong way? Thanks in advance!
Try it this way:
self.keyboardHeight.constant = -height;
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:animationDuration animations:^{
[self.view layoutIfNeeded];
}];
Remember this pattern because this should be the correct way to update constraint-based layouts (according to WWDC). You can also add or remove NSLayoutConstraint
s as long as you call setNeedsUpdateConstraints
after.
If you're using UITableViewController, keyboard size should be automatically accommodated by iOS to adjust the contentInsets. But if your tableView is inside a UIViewController, you probably wanted to use this:
KeyboardLayoutConstraint in the Spring framework. Simplest solution I've found so far.
Try the next code. In this case table view lays out at the bottom edge of the screen.
- (void)keyboardWillShow:(NSNotification *)notification { // UIKeyboardWillShowNotification
NSDictionary *info = [notification userInfo];
NSValue *keyboardFrameValue = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
CGRect keyboardFrame = [keyboardFrameValue CGRectValue];
BOOL isPortrait = UIDeviceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation);
CGFloat keyboardHeight = isPortrait ? keyboardFrame.size.height : keyboardFrame.size.width;
// constrBottom is a constraint defining distance between bottom edge of tableView and bottom edge of its superview
constrBottom.constant = keyboardHeight;
// or constrBottom.constant = -keyboardHeight - in case if you create constrBottom in code (NSLayoutConstraint constraintWithItem:...:toItem:...) and set views in inverted order
[UIView animateWithDuration:animationDuration animations:^{
[tableView layoutIfNeeded];
}];
}
- (void)keyboardWillHide:(NSNotification *)notification { // UIKeyboardWillHideNotification
NSDictionary *info = [notification userInfo];
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
constrBottom.constant = 0;
[UIView animateWithDuration:animationDuration animations:^{
[tableView layoutIfNeeded];
}];
}
The approach I took is to add a view which follows the size of the keyboard. Add it below your tableview, or text input or whatever and it will push things up when the keyboard appears.
This is how I set up the view hierarchy:
NSDictionary *views = @{@"chats": self.chatsListView, @"reply": self.replyBarView, @"fakeKeyboard":self.fakeKeyboardView};
[self.view addVisualConstraints:@"V:|-30-[chats][reply][fakeKeyboard]|" views:views];
And then the key bits of the keyboard-size-following view look like this:
- (void)keyboardWillShow:(NSNotification *)notification
{
// Save the height of keyboard and animation duration
NSDictionary *userInfo = [notification userInfo];
CGRect keyboardRect = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
self.desiredHeight = CGRectGetHeight(keyboardRect);
self.duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue];
[self animateSizeChange];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
self.desiredHeight = 0.0f;
[self animateSizeChange];
}
- (CGSize)intrinsicContentSize
{
return CGSizeMake(UIViewNoIntrinsicMetric, self.desiredHeight);
}
- (void)animateSizeChange
{
[self invalidateIntrinsicContentSize];
// Animate transition
[UIView animateWithDuration:self.duration animations:^{
[self.superview layoutIfNeeded];
}];
}
The nice thing about letting this particular view handle its resizing is that you can let the view controller ignore it, and you can also re-use this view any place in your app you want to shift everything up.
The full file is here:
https://gist.github.com/shepting/6025439