尝试做类似于Messages.app的行为,我有一个UIScrollView
并在其下面有一个文本字段,并尝试对其进行动画处理,以便当键盘出现时,所有内容都会向上移动到键盘上方将字段向上移动的约束(以及由于自动布局导致UIScrollView
的高度也发生变化),同时将contentOffset
设置为同时滚动到底部。
代码完成了想要的最终结果,但是在动画期间,当键盘动画开始时,滚动视图变为空白,然后内容从底部向上滚动,而不是从动画开始时的位置滚动
动画是这样的:
- (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];
}
该问题的视频可用here。
谢谢!
答案 0 :(得分:33)
这可能是UIKit中的一个错误。当同时更改size
和contentOffset
UIScrollView
时会发生这种情况。测试在没有自动布局的情况下是否也会发生此行为会很有趣。
我找到了解决此问题的两种方法。
在消息应用程序中可以看到,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 = 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];
}
此处self.bottomSpaceConstraint
是messageComposeView
和Bottom Layout Guide
之间的约束。 Here's the video showing它是如何运作的。
更新1: Here's my project's source on GitHub。这个项目有点简化。我应该考虑- (void)keyboardWillShow:(NSNotification *)notif
中通知中传递的选项。
不是一个确切的解决方案,但如果将其移动到完成块,滚动工作正常:
} completion:^(BOOL finished) { [self.collectionView setContentOffset:CGPointMake(0, self.collectionView.contentSize.height - self.collectionView.bounds.size.height) animated:YES]; }];
键盘显示需要0.25秒,因此动画的开头之间的差异可能会很明显。动画也可以按相反的顺序完成。
更新2:我也注意到OP的代码可以正常使用此更改:
CGPoint bottomOffset = CGPointMake(0, self.collectionView.contentSize.height - (self.collectionView.bounds.size.height - height));
但仅当contentSize
的{{1}}小于某个固定值(in my case around 800
时,但我的布局可能会略有不同)。
最后,我认为我在height
中提出的方法比调整Using contentInset (the Messages approach)
的方法要好。使用UICollectionView
时,我们还可以获得键盘下元素的可见性。它当然更适合iOS 7风格。
答案 1 :(得分:8)
我有一个类似的问题 - 当动画框架和偏移内容时,#34;跳跃"在动画到位之前 - 整个解决方案只是将UIViewAnimationOptionBeginFromCurrentState
添加到动画选项中。瞧!
答案 2 :(得分:1)
setNeedsUpdateConstraints
真的需要吗?是不是自动进行自动布局?
如果没有 - 我建议你单独使用所有调整大小的动画而不使用自动布局。看起来问题是当你尝试同时做两个动画时(但它不在同一时间)
答案 3 :(得分:1)
我不确定这是你想要的行为,但也许它可以让你在正确的方向上轻推:Github project
我所做的是设置两个约束,一个用于文本字段(到底部指南),另一个用于滚动视图(到文本字段)。
然后在调用动画时,我为两个元素的“center”属性设置动画,而不是contentOffset,并且我分别处理动画值和约束值。
最终结果如下:
答案 4 :(得分:1)
尝试删除[self.view layoutIfNeeded]
行,看看问题是否仍然存在,或者是否出现其他问题,如果出现问题,如果它们看起来有任何关联。
此外,在动画之前重置视图的位置始终是个好主意。因此,尝试在动画行之前设置正常偏移(甚至可能在那里调用layoutIfNeeded
方法)在开始动画之前将所有内容按顺序放置。
答案 5 :(得分:0)
我遇到了同样的问题,我可以通过注册NSNotifications
键盘/隐藏和显示来解决它。我提供相同的代码。希望它会对你有所帮助。
在你的.h类中声明BOOL isMovedUp
在ViewDidLoad
中// registering notifications for keyboard/hiding and showing
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide)
name:UIKeyboardWillHideNotification
object:nil];
}
#pragma mark-keyboard notifications
- (void)keyboardWillShow {
// Animate the current view out of the way
if (isMovedUp==YES){
} else {
[self setViewMovedUp:YES];
isMovedUp=YES;
}
}
- (void)keyboardWillHide {
if (isMovedUp==YES) {
[self setViewMovedUp:NO];
isMovedUp=NO;
}
}
//method for view transformation
-(void)setViewMovedUp:(BOOL)movedUp {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5]; // if you want to slide up the view
CGRect rect = self.winePopUpView.frame;
if (movedUp) {
//isKeyBoardDown = NO;
// 1. move the view's origin up so that the text field that
// will be hidden come above the keyboard
// 2. increase the size of the view so that the area
// behind the keyboard is covered up.
rect.origin.y -= 100;
//rect.size.height += 100;
} else {
// revert back to the normal state.
rect.origin.y += 100;
//rect.size.height -= 100;
//isKeyBoardDown = YES;
}
self.winePopUpView.frame = rect;
[UIView commitAnimations];
}