It might be a bug in UIKit. It happens when there's a simultaneous change of size
and contentOffset
of UIScrollView
. It'd be interesting to test if this behavior also happens without Auto Layout.
I've found two workarounds to this problem.
Using contentInset (the Messages approach)
As it can be seen in the Messages app, UIScrollView
's height doesn't change when a keyboard is shown - messages are visible under the keyboard. You can do it the same way. Remove constraint between UICollectionView
and the view that contains UITextField
and UIButton
(I'll call it messageComposeView
). Then add constraint between UICollectionView
and Bottom Layout Guide
. Keep the constraint between messageComposeView
and the Bottom Layout Guide
. Then use contentInset
to keep the last element of the UICollectionView
visually above the keyboard. I did it the following way:
- (void)updateKeyboardConstraint:(CGFloat)height animationDuration:(NSTimeInterval)duration {
self.bottomSpaceConstraint.constant = height;
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
CGPoint bottomOffset = CGPointMake(0, self.collectionView.contentSize.height - (self.collectionView.bounds.size.height - height));
[self.collectionView setContentOffset:bottomOffset animated:YES];
[self.collectionView setContentInset:UIEdgeInsetsMake(0, 0, height, 0)];
[self.view layoutIfNeeded];
} completion:nil];
}
Here self.bottomSpaceConstraint
is a constraint between messageComposeView
and Bottom Layout Guide
. Here's the video showing how it works.
UPDATE 1: Here's my project's source on GitHub. This project is a little simplified. I should've taken into consideration options passed in the notification in - (void)keyboardWillShow:(NSNotification *)notif
.
Performing changes in a queue
Not an exact solution, but scrolling works fine if you move it to the completion block:
} completion:^(BOOL finished) {
[self.collectionView setContentOffset:CGPointMake(0, self.collectionView.contentSize.height - self.collectionView.bounds.size.height) animated:YES];
}];
It takes 0.25s for the keyboard to show, so the difference between the beginnings of the animations might be noticeable. Animations can also be done in the reversed order.
UPDATE 2: I've also noticed that OP's code works fine with this change:
CGPoint bottomOffset = CGPointMake(0, self.collectionView.contentSize.height - (self.collectionView.bounds.size.height - height));
but only when contentSize
's height
is less than some fixed value (in my case around 800
, but my layout may be a little different).
In the end I think that the approach I presented in Using contentInset (the Messages approach)
is better than resizing UICollectionView
. When using contentInset
we also get the visibility of the elements under the keyboard. It certainly fits the iOS 7 style better.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…