如何正确的动画效果的UIScrollView contentOffset

问题描述 投票:16回答:3

我有UIScrollView的子类。其含量是可重复使用 - 约4或5次用于显示数百元件(滚动时再利用隐藏的对象,并跳转到当其需要看到他们的另一位置)

我需要:能够自动滚动我的滚动视图的任何位置。例如我的滚动视图显示第4,第5和第6元素,当我点击一些按钮,它需要滚动到30元。换句话说,我需要的UIScrollView的标准行为。

这工作正常:

[self setContentOffset:CGPointMake(index*elementWidth, 0) animated:YES];

但我需要一些定制。例如,改变动画的持续时间,添加一些代码来对动画的结束时执行。

显而易见的决定:

[UIView animateWithDuration:3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
    [self setContentOffset:CGPointMake(index*elementWidth, 0)];
} completion:^(BOOL finished) {
    //some code
}];

但我有一些动作连接到滚动事件,所以现在所有的人都在动画块,它会导致所有子视图的帧动画太(感谢一些可重复使用的元素个个动画不是我想要的)

现在的问题是:如何让自定义动画(其实我需要自定义工期,在结束和BeginFromCurrentState选择操作)的内容,而动画的所有码偏移,连接scrollViewDidScroll事件?

UPD:由于Andrew's answer(第一部分)我解决了内部scrollViewDidScroll动画问题:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    [UIView performWithoutAnimation:^{
        [self refreshTiles];
    }];
}

scrollViewDidScroll必须(对我而言)执行动画的每一帧,就像是在的情况下,

[self setContentOffset:CGPointMake(index*elementWidth, 0) animated:YES];

然而,现在它在动画开始执行一次。

我该如何解决这个问题?

ios cocoa-touch uiview uiscrollview uiviewanimation
3个回答
18
投票

你尝试同样的方法,但在scrollViewDidScroll禁用动画?

在iOS 7,你可以尝试包装你在scrollViewDidScroll代码

[UIView performWithoutAnimation:^{
       //Your code here
    }];

在以前的IOS版本,你可以尝试:

  [CATransaction begin];
    [CATransaction setDisableActions:YES];
     //Your code here
    [CATransaction commit];

更新:

不幸的是这就是你打的整个事情的艰难的部分。 setContentOffset:调用委托只有一次,这相当于setContentOffset:animated:NO,这再次调用它只是一次。

setContentOffset:animated:YES调用委托作为动画改变滚动视图的边界和你想要的,但你不希望所提供的动画,所以解决这个问题的唯一办法,我可以想出是要逐步改变滚动型的contentOffset,使动画系统不只是跳转到终值,因为是在目前的情况下。

要做到这一点,你可以看看关键帧动画,像这样的iOS 7:

[UIView animateKeyframesWithDuration:duration delay:delay options:options animations:^{
    [UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.5 animations:^{
       [self setContentOffset:CGPointMake(floorf(index/2) * elementWidth, 0)];
    }];
    [UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.5 animations:^{
        [self setContentOffset:CGPointMake(index*elementWidth, 0)];
    }];
} completion:^(BOOL finished) {
    //Completion Block
}];

这将让你两个更新,当然你可以使用一些数学和循环加起来的这些大量的多与适当的时机。

在以前的IOS版本,你就必须下降到CoreAnimation关键帧动画,但它基本上是一个有点不同的语法同样的事情。

方法2:您可以尝试投票滚动视图的表示层与一个计时器,你开始在动画开始时的任何变化,因为不幸的是,表示层的性能没有观察到志愿。或者你也可以在该层的一个子类使用needsDisplayForKey得到通知时的范围变化,但会需要一些工作来设置,它并导致重绘,这可能会影响性能。

方法3:将解剖动画是YES尝试拦截大干快上滚动视图设置动画,改变其参数时究竟发生了什么了滚动,但因为这将是最哈克,易碎由于苹果公司的变化和最棘手的方法,我不会进入它。


5
投票

一个很好的办法做到这一点是与AnimationEngine库。这是一个非常小的库:六个文件,有三个,如果你想阻尼弹簧的行为。

在幕后,它使用一个CADisplayLink运行一次你的动画块每帧。你得到一个干净的基于块的语法,易于使用,和一堆interpolationeasing functions为您节省时间。

动画contentOffset

startOffset = scrollView.contentOffset;
endOffset = ..

// Constant speed looks good...
const CGFloat kTimelineAnimationSpeed = 300;
CGFloat timelineAnimationDuration = fabs(deltaToDesiredX) / kTimelineAnimationSpeed;

[INTUAnimationEngine animateWithDuration:timelineAnimationDuration
                                   delay:0
                                  easing:INTULinear
                              animations:^(CGFloat progress) {
                                    self.videoTimelineView.contentOffset = 
                                         INTUInterpolateCGPoint(startOffset, endOffset, progress);
                              }
                             completion:^(BOOL finished) {
                                   autoscrollEnabled = YES;
                             }];

1
投票

尝试这个:

UIView.animate(withDuration: 0.6, animations: {
        self.view.collectionView.contentOffset = newOffset
        self.view.layoutIfNeeded()
    }, completion: nil)
© www.soinside.com 2019 - 2024. All rights reserved.