ios 动画UIScrollView contentInset导致跳转断断续续

vom3gejh  于 2023-03-09  发布在  iOS
关注(0)|答案(8)|浏览(220)

我实现了一个自定义的刷新控件(我自己的类,而不是子类),由于某种原因,自从转到iOS 8后,设置滚动视图(具体来说,UICollectionView)的contentInset来启动刷新动画会导致奇怪的跳跃/断断续续。

- (void)containingScrollViewDidScroll:(UIScrollView *)scrollView
{
    CGFloat scrollPosition = scrollView.contentOffset.y + scrollView.contentInset.top;

    if( scrollPosition > 0 || self.isRefreshing )
    {
        return;
    }

    CGFloat percentWidth = fabs( scrollPosition ) / self.frame.size.height / 2;

    CGRect maskFrame = self.maskLayer.frame;

    maskFrame.size.width = self.imageLayer.frame.size.width * percentWidth;

    [CATransaction begin];
    [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
    self.maskLayer.frame = maskFrame;
    [CATransaction commit];
}

- (void)containingScrollViewDidEndDragging:(UIScrollView *)scrollView
{
    if( ( self.maskLayer.frame.size.width >= self.imageLayer.frame.size.width ) && !self.isRefreshing )
    {
        self.isRefreshing = YES;
        [self setLoadingScrollViewInsets:scrollView];
        [self startAnimation];
        [self sendActionsForControlEvents:UIControlEventValueChanged];
    }
}

- (void)setLoadingScrollViewInsets:(UIScrollView *)scrollView
{
    UIEdgeInsets loadingInset = scrollView.contentInset;
    loadingInset.top += self.frame.size.height;

    UIViewAnimationOptions options = UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState;

    [UIView animateWithDuration:0.2 delay:0 options:options animations:^
    {
        scrollView.contentInset = loadingInset;
    }
    completion:nil];
}

基本上,一旦用户释放刷新控件,我就将contentInset动画化到刷新控件的高度。我认为动画可以减少断断续续/跳跃,iOS 7就是这样做的。但在iOS 8中,当scrollView从拖动状态释放时,而不是仅仅动画化到contentInset,滚动视图内容会从释放点快速向下跳跃。然后流畅地动画。我不确定这是iOS 8的bug还是什么。我还试着添加了:

scrollView.contentOffset = CGPointZero;

在动画块中,这并没有改变任何东西。
有人有什么想法吗?任何帮助都将不胜感激。谢谢!

cvxl0en2

cvxl0en21#

我将动画块的方法更改为:

- (void)setLoadingScrollViewInsets:(UIScrollView *)scrollView
{
    UIEdgeInsets loadingInset = scrollView.contentInset;
    loadingInset.top += self.view.frame.size.height;

    CGPoint contentOffset = scrollView.contentOffset;

    [UIView animateWithDuration:0.2 animations:^
    {
        scrollView.contentInset = loadingInset;
        scrollView.contentOffset = contentOffset;
    }];
}
oknwwptz

oknwwptz2#

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
    CGPoint point = scrollView.contentOffset;

    static CGFloat refreshViewHeight = 200.0f;

    if (scrollView.contentInset.top == refreshViewHeight) {
        //openning
        if (point.y>-refreshViewHeight) {
            //will close
            //必须套两层animation才能避免闪动!
            [UIView animateWithDuration:0 animations:NULL completion:^(BOOL finished) {
                [UIView animateWithDuration:0.25 animations:^{
                    scrollView.contentOffset = CGPointMake(0.0f, 0.0f);
                    scrollView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f);
                } completion:NULL];
            }];

        }
    }
    else{
        //closing
        static CGFloat openCriticalY = 40.0f;//会执行打开的临界值
        if(point.y<-openCriticalY){
            //will open
            [UIView animateWithDuration:0 animations:NULL completion:^(BOOL finished) {
                [UIView animateWithDuration:0.25 animations:^{
                    scrollView.contentInset = UIEdgeInsetsMake(refreshViewHeight, 0.0f, 0.0f, 0.0f);
                    scrollView.contentOffset = CGPointMake(0.0f, -refreshViewHeight);
                } completion:NULL];
            }];
        }
    }
}

你可以试试这个,关键是用了两个动画。

8ehkhllq

8ehkhllq3#

ryanthon的答案可以解决这个问题,在我的iOS9.0应用程序中,甚至不需要动画块就可以解决这个问题,只需在设置contentInset之后重置contentOffset就可以了。
如果你想在隐藏刷新视图时控制tableView的滚动速度,user3044484的答案中的双动画块技巧将发挥神奇的作用,一个动画块是不够的。

if (self.tableView.contentInset.top == 0)
{
    UIEdgeInsets tableViewInset = self.tableView.contentInset;
    tableViewInset.top = -1.*kTableHeaderHeight;
    [UIView animateWithDuration: 0 animations: ^(void){} completion:^(BOOL finished) {
        [UIView animateWithDuration: 0.5 animations:^{
        //no need to set contentOffset, setting contentInset will change contentOffset accordingly.
            self.tableView.contentInset = tableViewInset;  
        }];
    }];
}
ktecyv1j

ktecyv1j4#

我有同样的问题,并移动我的动画块到...
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
虽然不是很完美,但我还不能确定问题所在。看起来scrollView的-layoutSubviews方法在iOS8下被调用得更频繁了,导致动画跳动。

dauxcl2d

dauxcl2d5#

两种解决方案:
1.开始加载时禁用反弹,完成加载时启用反弹
1.设置内容插入后当开始加载,请设置内容偏移量与一个常量值

s4n0splo

s4n0splo6#

本人已作出决议:

  • 当ContentInset更新为contentOffsetY时,禁用反弹。
  • 跟踪contentOffset并更新到表视图ContentInset。
var contentOffsetY:CGFloat = 100
    func tracScrollContent(location: UIScrollView) {
        if(location.contentOffset.y  == -contentOffsetY ) {
            tblView.isScrollEnabled = true
            tblView.bounces = false
            tblView.contentInset = UIEdgeInsets(top: contentOffsetY, left: 0, bottom: 0, right: 0)
        }else if(location.contentOffset.y >= 0 ){
            tblView.bounces = true
            tblView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        }
    }

Github Demo Swift-3

laawzig2

laawzig27#

不幸的是,在- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView中制作动画对我来说不起作用。滚动视图还是会反弹。
根据Bounds automatically changes on UIScrollView with content insets,这似乎是一个iOS8的错误给我...

yws3nbqq

yws3nbqq8#

基于answer by @ryanthon.似乎没有必要动画的变化,至少现在在2023年).在iOS 15验证.

let offset = scrollView.contentOffset.y
scrollView.contentInset.top = pullToRefreshInset
scrollView.contentOffset.y = offset

相关问题