iOS新浪新闻首页卡片滚动特效实现浅谈
公司项目采用了这个特效。
Leader非常中意这个特效,说要在我们app中使用,问我能不能实现,我一看这个特效,卧槽,涉及了好多技术点,实现难度很大。如果是自己做特效还好,模仿别人的特效,做的粗糙没什么,如果要做得好,就得考虑好多细节。身为男人,怎么能说不行,自己给自己安排时间,偷偷搞,经过了几天的艰苦奋战,终于完成了,开森。
能够完成这个特效,感谢她,感谢他,感谢一如既往支持我的人。
进入正题
先看下新浪的特效(版本 v6.6.0)
新浪新闻首页卡片滚动特效
先分析一下这个滚动特效,有3种方式控制滚动:
1、1个是手指上下滑动整个表格,中间的滚动视图会跟着一起动。
2、1个是左右滑动中间的滚动视图,滚动视图会快速滑动。
3、1个是左右晃动手机,根据角度不同,滚动视图跟着一起动。
需要解决很多问题:
1、中间的滚动视图是一块一块移动的,停止时距离中间最近的卡片会自动滑动到中间,居中对齐。
2、中间的滚动视图在滑动的时候发现卡片是叠在一起的,中间的在上层,其他部分在下层,根据距离中间位置的远近来区别上下层。
3、中间的滚动视图在滑动的时候发现卡片大小不一致,中间的最大,越靠近边框越小。
4、中间的滚动视图在滑动的时候发现滑动的距离和卡片移动的距离并不是成正比,而是按照不断变化的加速度移动的。
5、中间的滚动视图滑到左右边缘时视图透明度改变。
6、循环滚动方案的实现
7、上下滑动表格时,中间的滚动视图要跟着一起滑动,上滑时向左移动,下滑时向右移动。
8、左右晃动手机时,中间的滚动视图要跟着一起滑动,向左晃动时卡片向左移动,向右晃动时卡片向右移动。
9、需要保证刚才提到的3种控制方式互不干扰。
大问题大概就是这几个,当然还有很多小问题,就不一一列出来了。
那么,说干就干
完成这个特效之前,首先是要选择实现工具,实现工具的选择很重要,相当于方向,方向选对了,才能使上大力气。
实现工具有3种:
1、自定义滚动视图
2、UIScrollView
3、UICollectionView
第一种,使用自定义滚动视图,亲自去Github上面找了不少关于自定义滚动视图的demo,觉得难度不是一般的难,而是二班的难呐,想着新浪的iOS开发水平应该也没那么强,肯定不是这种方案,就Pass了第一种。
那么看下第二种,使用UIScrollView,苹果自己弄的UIScrollView,可以分页是可以分页,但是只能整个视图分页,无法做到区域分页,看了不少的Demo,实现效果也差强人意,无奈Pass了第二种。
那么第三种呢?使用UICollectionView,在网上找到了不少可以提供灵感的Demo,感觉上是可行的,然后伟哥给了类似的Demo告诉我这种方案的可行性。可以的,那就用这种方式搞起。
确定好了方向之后就全力以赴了。
问题1:中间的滚动视图是一块一块移动的,停止时距离中间最近的卡片会自动滑动到中间,居中对齐。
UICollectionView有个布局类叫做UICollectionViewFlowLayout,可以在里面实现卡片效果,通过计算每个可见Cell到中间位置,来计算出偏移量,得出CollectionView最终停留的位置,实现自动滑动到中间,居中对齐的效果。实现以下方法:
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
问题2:中间的滚动视图在滑动的时候发现卡片是叠在一起的,中间的在上层,其他部分在下层,根据距离中间位置的远近来区别上下层。
看UICollectionViewFlowLayout布局文件,通过计算可见Cell到中心点位置,来确定Cell的位置,达到叠加的效果。上下层的概念则是通过z轴的不同来实现,搞过游戏的朋友可能会更清楚一些,三维坐标系中有x轴,y轴,z轴,通过z轴确定视图的上下层次。实现以下方法:
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)oldBounds
问题3:中间的滚动视图在滑动的时候发现卡片大小不一致,中间的最大,越靠近边框越小。
在UICollectionViewFlowLayout布局文件中解决,设置好垂直缩放系数,根据距离的远近进行放大处理,距离中间的放到最大。
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
问题4:中间的滚动视图在滑动的时候发现滑动的距离和卡片移动的距离并不是成正比,而是按照不断变化的加速度移动的。
在UICollectionViewFlowLayout布局文件中解决,根据可见Cell到中心的不同位置设置移动距离,不同的距离使用不同的加速度。
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
问题5:中间的滚动视图滑到左右边缘时视图透明度改变。
在UICollectionViewFlowLayout布局文件中解决,通过计算边缘距离,来动态改变透明度。
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
问题7:上下滑动表格时,中间的滚动视图要跟着一起滑动,上滑时向左移动,下滑时向右移动。
需要在表格类中滚动代理方法里面设置滚动时设置UICollectionView的ContentOffset偏移量,然后在scrollViewDidEndDragging和scrollViewDidEndDecelerating两个方法中设置滚动结束后,UICollectionView的ContentOffset偏移量,需要计算到屏幕中间位置的偏移量进行偏移。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
问题8:左右晃动手机时,中间的滚动视图要跟着一起滑动,向左晃动时卡片向左移动,向右晃动时卡片向右移动。
晃动手机,涉及到硬件支持。iPhone手机内配置了各种传感器,其中一个就是重力感应。通过了解重力感应,知道了加速器,使用加速器来控制晃动偏移量,发现效果不好,抖动特别厉害,在网上也没有特别好的例子说明。那么改用陀螺仪,苹果的陀螺仪做的非常精致,效果也很好,非常稳定,但是又发现了新问题,就是运动停止不了。最终在加速器和陀螺仪的配合之下,终于完美的解决了问题。
- (void)startGyroUpdatesToQueue:(NSOperationQueue *)queue withHandler:(CMGyroHandler)handler
- (void)startAccelerometerUpdatesToQueue:(NSOperationQueue *)queue withHandler:(CMAccelerometerHandler)handler
问题9:需要保证刚才提到的3种控制方式互不干扰。
涉及到条件判断,不仅需要在表格的UIScrollView代理方法做控制,还要在UICollection代理方法做控制,保证效果互不干扰。
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
完成之后的效果如下:
我做的卡片滚动特效
如果是按照新浪新闻的还简单一点,但是问题是,我们的产品总是会搞点花样,比如修改透明度,修改圆角呀,等等,所以实现的难度就增加了不少。总之,兵来将挡水来土掩,完成就是了。
文末补充:
很高兴,通过我的思路,有人已经把这个特效做出来了,先不说实现代码质量的好坏,但实现了是好事。有需要的朋友可以去看看他的文章,以及他的demo,希望有更多的人向他学习,自己动手丰衣足食。
-------------------------------我是分割线-------------------------------