前几天在gitHub看到个不错的效果,就是DaiExpandCollectionView,效果如图:
所以赶紧下下来源码看看他怎么实现的,打开源码看了半天,发现他没写什么关于动画的代码啊。。。
经高人指点,才明白原来他是利用了UICollectionViewFlowLayout的隐式动画!
所谓隐式动画,就是系统自带的动画了,其实也不是什么高大上的东西,关键是我怎么就没想到可以这么用!
研究了半天人家的源码,基本了解了他实现的思路,然后发现他的这个库用起来比较不方便,需要继承他的collectionView,而且不能自定义cell大小,不能很好的适配各种尺寸的屏幕,
但这个库我确实很喜欢,所以就自己仿写了一个,就是LXMExpandLayout,顺便解决下上面发现的问题,地址在这里: https://github.com/Phelthas/LXMExpandLayout
效果如图:
还是来说说思路和原理的问题
基本思路是:
继承系统的UICollectionViewFlowLayout,利用UICollectionViewFlowLayout已有的各种属性和效果,对其做出适当的修改已达到自己想要的效果
具体来说就是:
1,重载 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;方法,
用 [super layoutAttributesForElementsInRect:newRect]获取各个元素在 UICollectionViewFlowLayout中的layoutAttributes。
然后修改其属性
2,属性可不是随便改的,这个得给原作者点个赞,他想出来的这个规则,让这个属性的修改简单了好几倍。
这个规则就是:选中的item放大到这个item的旁边只能放得下一个没有放大的item!!!
这个规则的牛X之处在于:这样放大的item的右边,刚好可以放得下原来跟他同一行的剩下的item,进而需要修改属性的item就是原来跟需要放大的item同一行的item,剩下item,只需要简单的上下平移或者根本不用动!!!
3,知道了这个规则,就有了方向,可以大胆动手改了,选中的item用transform属性做平移和放大,其他的简单修改frame就可以了
这里怎么取出来和选中item同一行的其他item呢?
有个不错的方法,就是: self.sameRowItemArray = [super layoutAttributesForElementsInRect:CGRectMake(0, selectedAttributes.frame.origin.y, self.collectionViewWidth, self.itemHeight)];注意是super的方法,不是self~~
4,关键的时刻到了,怎么让collectionView执行隐形动画呢?
这个也是从原作者那里学到的,就是调用系统的 - (void)performBatchUpdates:(void (^)(void))updates completion:(void (^)(BOOL finished))completion;方法。我把它封装成了一个collectionView的分类,方便配合我的LXMExpandLayout使用。
这个方法会使collectionView重新调用其layout去重新布局,而且是带动画的哦~~
5,直接调用performBatchUpdates方法的话,动画是线性的,那怎么样才能有弹簧的效果呢?
这里又从原作者那里学到一招:用一个UIView animation的block将则个performBatchUpdates包起来
这样就可以用UIView的animation动画代替performBatchUpdates的默认动画。利用iOS7自带的弹簧动画方法
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);
效果杠杠的~~
其他还有一些细节需要注意的,代码里我已经写了注释了,这里就不在说了
具体这个库怎么使用,也已经写在gitHub上了,也就不罗嗦了。
有问题欢迎留言讨论~~