UIScrollView自定义减速动画

819 阅读3分钟

需求

拖动 UIScrollView 结束后,会有个减速动画,领导对这个减速动画不满意,觉得太慢,要改快一点。

修改 UIScrollView 减速动画速度

修改 decelerationRate 为 UIScrollViewDecelerationRateFast

但是动画即将结束的部分还是会有一个极慢的过程。

后来发现,UIScrollView 的这个减速动画非常好的模拟了用户手指拖动带来的惯性导致列表继续滑动的过程。

但是我们的场景是卡尺效果,就显得有些不合适。只能放弃利用这个动画了。

卡尺效果:

未命名文件.png

如上图,卡尺效果就是手指拖动结束后,使列表继续滑动,一直滑动到某一个视频的顶部顶到页面最顶端

使用系统动画

UIView 动画 和 [UIScrollView setContentOffset:animated:]

这两个动画都有些卡顿,打印了 didScroll 方法后发现,原因是:

这两个动画的速度都是由小到大,再变小的。

但是用户手指拖动结束的那一刹那速度是比较大的,突然速度变小,就有卡顿的感觉了。

可以通过设置动画的 options 修改动画的速度:

UIViewAnimationOptions 里有以下可用的值:
UIViewAnimationOptionCurveEaseInOut            = 0 << 16, // default
UIViewAnimationOptionCurveEaseIn               = 1 << 16,    
UIViewAnimationOptionCurveEaseOut              = 2 << 16,
UIViewAnimationOptionCurveLinear               = 3 << 16,

无论怎么改都是有卡顿的感觉,因为这几个动画全都是 Curve,抛物线的,所以动画速度都是由小变大,再变小的。

CAAnimation

看 CAAnimation 的 timingFunction 取值范围有这些,好像可以做出不是抛物线的动画。

CA_EXTERN CAMediaTimingFunctionName **const** kCAMediaTimingFunctionLinear;
CA_EXTERN CAMediaTimingFunctionName **const** kCAMediaTimingFunctionEaseIn;
CA_EXTERN CAMediaTimingFunctionName **const** kCAMediaTimingFunctionEaseOut;
CA_EXTERN CAMediaTimingFunctionName **const** kCAMediaTimingFunctionEaseInEaseOut;
CA_EXTERN CAMediaTimingFunctionName **const** kCAMediaTimingFunctionDefault;

但是 CAAnimation 只能对 CALayer 的属性做动画,那只能对 bounds 做动画。

会导致以下两个问题:

  1. 列表的滚动条会随着一起滚动,等动画结束后,才会回到正确的位置
  2. 有时候滚动出下一个 cell 时,会显示白色,等动画结束后,才会展示正确的 cell

原因是:

虽然 UITableView 的滚动利用的就是 UIView 的 bounds,但是在调用 setContentOffset 时,UITableView 会同时修改滚动条和加载新的 cell,但是直接修改 bounds,并不会做这些,才导致了问题。

这个问题没法解决,所以只能放弃这个方向。

使用 CADisplayLink

后来采用了 CADisplayLink 注册监听,根据时长等信息计算此时列表预期的位置,将列表通过 setContentOffset: 移动到预期位置的方式,完成了这个动画。

具体代码可见:

github.com/qyz777/scro…

不足之处:

这种方式需要自己计算某时间点列表的位置,最理想的结果是动画的起始速度与用户手指离开前的速度一样,但是这样十分复杂,我搞不定这个公式,所以就做了个跟手指离开前的速度没关系的动画。

效果如:

今日头条数码频道,点击某个视频进入视频列表页,视频列表页的拖动效果。