UIScrollView 的 height 和 contentOffset 动画“跳跃"底部的内容

时间:2022-11-03
本文介绍了UIScrollView 的 height 和 contentOffset 动画“跳跃"底部的内容的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

尝试做一些类似于 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.当 UIScrollViewsizecontentOffset 同时发生变化时会发生这种情况.如果没有自动布局,测试这种行为是否也会发生会很有趣.

我找到了两个解决这个问题的方法.

使用 contentInset(消息方法)

正如在消息应用程序中所见,UIScrollView 的高度在显示键盘时不会改变 - 消息在键盘下可见.你可以这样做.移除 UICollectionView 与包含 UITextFieldUIButton 的视图之间的约束(我将其称为 messageComposeView).然后在 UICollectionViewBottom Layout Guide 之间添加约束.保持 messageComposeViewBottom Layout Guide 之间的约束.然后使用 contentInsetUICollectionView 的最后一个元素保持在键盘上方.我是这样做的:

- (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.bottomSpaceConstraintmessageComposeViewBottom 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));

但仅当 contentSizeheight 小于某个固定值 (在我的例子中是 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 动画“跳跃"底部的内容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

上一条:在 UIScrollView 和 UIView 的组件上检测触摸事件 [放置在 UIScrollView 内] 下一条:使用窥视相邻页面分页 UIScrollView

相关文章

最新文章