问题描述
尝试做一些类似于 Messages.app 的行为,我有一个 UIScrollView
并在其下方有一个文本字段,并尝试对其进行动画处理,以便当键盘出现时,所有内容都向上移动到使用将字段向上移动的约束的键盘(并且 UIScrollView
的高度也会由于自动布局而发生变化)并同时将 contentOffset
设置为滚动到底部时间.
代码完成了想要的最终结果,但是在动画期间,当键盘动画开始时,滚动视图变为空白,然后内容从底部向上滚动,而不是从动画开始时的位置滚动.
动画是这样的:
- (void)updateKeyboardConstraint:(CGFloat)height animationDuration:(NSTimeInterval)duration {self.keyboardHeight.constant = -高度;[self.view setNeedsUpdateConstraints];[UIView animateWithDuration:持续时间延迟:0 选项:UIViewAnimationOptionBeginFromCurrentState 动画:^{[self.view layoutIfNeeded];self.collectionView.contentOffset =CGPointMake(0, self.collectionView.contentSize.height - self.collectionView.bounds.size.height);} 完成:无];}
此处提供了该问题的视频.
谢谢!
可能是 UIKit 的 bug.当 UIScrollView
的 size
和 contentOffset
同时发生变化时会发生这种情况.如果没有自动布局,测试这种行为是否也会发生会很有趣.
我找到了两个解决这个问题的方法.
使用 contentInset(消息方法)
正如在消息应用程序中所见,UIScrollView
的高度在显示键盘时不会改变 - 消息在键盘下可见.你可以这样做.移除 UICollectionView
与包含 UITextField
和 UIButton
的视图之间的约束(我将其称为 messageComposeView
).然后在 UICollectionView
和 Bottom Layout Guide
之间添加约束.保持 messageComposeView
和 Bottom Layout Guide
之间的约束.然后使用 contentInset
将 UICollectionView
的最后一个元素保持在键盘上方.我是这样做的:
- (void)updateKeyboardConstraint:(CGFloat)height animationDuration:(NSTimeInterval)duration {self.bottomSpaceConstraint.constant = 高度;[UIView animateWithDuration:持续时间延迟:0 选项:UIViewAnimationOptionBeginFromCurrentState 动画:^{CGPoint bottomOffset = CGPointMake(0, self.collectionView.contentSize.height - (self.collectionView.bounds.size.height - height));[self.collectionView setContentOffset:bottomOffset 动画:YES];[self.collectionView setContentInset:UIEdgeInsetsMake(0, 0, height, 0)];[self.view layoutIfNeeded];} 完成:无];}
这里的self.bottomSpaceConstraint
是messageComposeView
和Bottom Layout Guide
之间的约束.视频展示它是如何工作的.更新 1: 这是我在 GitHub 上的项目源代码.这个项目有点简化.我应该考虑在 - (void)keyboardWillShow:(NSNotification *)notif
中的通知中传递的选项.
在队列中执行更改
不是一个精确的解决方案,但如果将其移动到完成块,滚动效果很好:
<块引用>} 完成:^(BOOL 完成){[self.collectionView setContentOffset:CGPointMake(0, self.collectionView.contentSize.height - self.collectionView.bounds.size.height) 动画:YES];}];
键盘显示需要 0.25 秒,因此动画开始之间的差异可能很明显.动画也可以以相反的顺序完成.
更新 2: 我还注意到 OP 的代码在此更改下运行良好:
CGPoint bottomOffset = CGPointMake(0, self.collectionView.contentSize.height - (self.collectionView.bounds.size.height - height));
但仅当 contentSize
的 height
小于某个固定值 (在我的例子中是 800
,但我的布局可能有点不同.
最后我认为我在使用 contentInset(消息方法)
中提出的方法比调整 UICollectionView
的大小要好.使用 contentInset
时,我们还可以获得键盘下元素的可见性.它当然更适合 iOS 7 风格.
Trying to do something similar to the Messages.app's behavior, I have a UIScrollView
and beneath it a text field, and trying to animate it so that when the keyboard appears everything is moved up above the keyboard using a constraint that moves the field up (and the UIScrollView
's height changes as well due to autolayout) and also setting the contentOffset
to scroll to the bottom at the same time.
The code accomplishes the wanted end-result, but during the animation right when the keyboard animation begins the scroll view becomes blank and then the content scrolls up from the bottom, instead of scrolling from the position it was in when the animation started.
The animation is this:
- (void)updateKeyboardConstraint:(CGFloat)height animationDuration:(NSTimeInterval)duration {
self.keyboardHeight.constant = -height;
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
[self.view layoutIfNeeded];
self.collectionView.contentOffset =
CGPointMake(0, self.collectionView.contentSize.height - self.collectionView.bounds.size.height);
} completion:nil];
}
A video of the problem is available here.
Thanks!
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.
这篇关于UIScrollView 的 height 和 contentOffset 动画“跳跃"底部的内容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!