多任务卡片动画实现原理
实现效果
控件 — UICollectionView
这个动画是用 UICollectionView
实现的,简单讲下 UICollectionView
的工作原理。这里用到的 UICollectionView
也就3部分:ViewController
(简称VC)、UICollectionViewCell
、UICollectionViewLayout
。
ViewController
:
在VC里,UICollectionView
的用法跟UITableView
的用法类似。这里的初始化方法与UITableview
有所不同,多了个collectionViewLayout
属性,每个collectionView
都会绑定一个UICollectionViewLayout
对象,collectionView
根据这个layout
对象来布局cell
。UICollectionViewCell
:
这里用的Cell
实现起来和UITableViewCell
没什么大区别,我们只要实现它的initwithFrame
的初始化方法即可,然后实现你想要的布局。
UICollectionViewLayout
:
UICollectionViewLayout
UICollectionViewLayout
是让 UICollectionView
千变万化的精髓所在,所以上面的动画的重点也就是在 layout
里实现的。
系统提供了一个 UICollectionViewFlowLayout
,是一个流式布局,可以通过设置 scrollDirection
来指定滚动方向,如果这个系统提供的布局不能满足我们的需求,那我们就要自己实现一个 UICollectionViewLayout
的子类来达到我们想要的效果了,接下来说下自定义的 layout
需要重写哪几个方法。
1 | // 当layout第一次展示时调用,显式或隐式地调用invalidatedlayout也会调用prepareLayout,During each layout update, the collection view calls this method first to give your layout object a chance to prepare for the upcoming layout operation |
动画实现细节
上面是关于 UICollectionView
的一些初步介绍,还有很多使用细节待君探索。接下来说下我们这个动画的实现细节吧。
上面的动画中,包括了位移、缩放、透明度三个方面的变化,我们先来谈谈重点的位移部分吧。
思路:动画的效果是,随着手指的滑动, cell
从上往下慢慢位移,位移的过程中速度越来越快;相反,如果往上滑动, cell
移动的速度就慢慢变小。这里的自变量是 collectionView.contentOffset.y
(竖直方向),因变量是 cell.center.y
(或者说 cell.frame.origin.y
),所以需要为他们之间找一个函数,这个函数需要满足上面的动画效果。
根据变化的规律,先确定一个初步的函数:
它符合从小到大变化递增的特性,可以通过一些在线工具查看函数的图像来比较直观的看到函数的变化规律
可以看到这个函数是有两段的,那我们需要的只是左边这部分,因为当我们手指下滑的时候, collectionView.contentOffset.y
实际上是变小的,而 cell.origin.y
却是在变大的,所以左边半部分的变化正是我们想要的。
然而
并不能满足要求,需要将它①向右拉伸m个点、②向上拉伸n个点,也就是要构造一个函数:
这里解释一下m、n的值的含义。
将
做上述两步变化,m = 600,n = 500,生成如下函数
函数与 x 轴交点是 (600,0),与 y 轴交点是 (0,500)。
(0,500) 比较好理解,就是当 collectionView.contentOffset.y
等于 0 的时候, cell
对应的y坐标为 500。
(600,0) 也不难理解,就是当我们手指往上滑 600 个点,使 collectionView.contentOffset.y=600
时,这个时候 cell.origin.y
会等于 0。
函数的具体应用
定义第 0 个 cell
的位移函数
假设第 0 个 cell
(简称 cell0
)对应函数的 m、n 分别为 n0 = 250,m0 = 1000,即当 collectionView.contentOffset.y=0
时, cell0.origin.y=250
;当我们往上滑1000个点, collectionView.contentOffset.y=1000
时, cell0.origin.y=0
。同理,
ni 则表示当 contentOffset.y=0
时,第 i 个 cell
的 y 坐标。mi 则表示当 contentOffset.y=mi
时,第 i 个 cell
的 y 坐标为 0。
所以对于第 0 个 cell
,我们可以给出一个函数来计算它的 y 坐标:
这里 x 是指 collectionView.contentOffset.y
,y0 是指 cell0.origin.y
;
生成第 i 个 cell 的位移函数
定义了cell0 的位置函数,就可以以一定规律生成 cell1、cell2…… 的位置函数了,也就是生成每个cell 的 m、n 值。
mi 的计算
我们可以定义,当手指往下滑动140个点时,第1个 cell 会运动到第2个 cell的位置,以此类推,每个 cell
会运动到下一个 cell
的位置。所以我们定义 $m_i=m_{i-1}+140$ 也就是 $m_i=m_0 + itimes140$; (ps:这里140决定了 cell 之间的间距,当然可以根据需求改变这个值来调整视觉效果)
ni 的计算
需要通过 $y_0=((1000-x)/1000)^4times250$ 来计算。我们知道,当 x=0 时,$y_0=n_0=250$。在上一步计算时,我们定义了手指往下滑140时第0个 cell
会运动到第1个 cell
的位置,也就是说 cell1
的位置 $n_1$ 可以由 $y_0=((1000-x)/1000)^4times250$ 得到,**这里 x 的值应该是手指从 0 下滑140个点,也就是 collectionView.contentOffset.y=-140
,所以x=(-140)**。(ps:往下滑动时 contentOffset.y
是递减的,所以这里的x是负的140。)所以
同理,可以推出 ni 的公式:
、
yi 的公式
ok,mi 和ni 都可由 m0、n0 得到,那么 yi 的公式
就可以转化成 m0 和 n0 的表达式,即
虽然这个函数看起来挺长的,但是其中 m0和n0 都是我们定的初始值,140 也是我们定义的常量。变量 x 就是 contentOffset.y
。所以到此我们已经能根据手指的滑动,计算出每个 cell
的 y 坐标,从而实现了这个滚动动画。
公式体现在下面的函数里
1 |
|
转自
- 仿多任务卡片动画