本文主要是介绍UICollectionView 的研究之二 :自定义 UICollectionViewFlowLayout,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
UICollectionView 实现各式复杂布局核心在于 UICollectionViewLayout,需要我们去自定义实现。
通过各种layout 的自定义实现,以及它们之间的切换。可以实现一些酷炫的布局,例如
(图片选自:http://www.cnblogs.com/markstray/p/5822262.html)
Cover Flow 布局
堆叠布局
圆形布局
关于需要重写方法的描述
自定义布局需要重写以下四个方法
(文字描述取自:https://www.cnblogs.com/wangliang2015/p/5388658.html 和 https://www.cnblogs.com/hissia/p/5723629.html):
- 作用:在这个方法中做一些初始化操作
- 注意:子类重写prepareLayout,一定要调用[super prepareLayout]
- (void)prepareLayout;
- 作用:
- 这个方法的返回值是个数组
- 这个数组中存放的都是UICollectionViewLayoutAttributes对象
- UICollectionViewLayoutAttributes对象决定了cell的排布方式(frame等)
- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect;
- 作用:如果返回YES,那么collectionView显示的范围发生改变时,就会重新刷新布局
- 一旦重新刷新布局,就会按顺序调用下面的方法:
- prepareLayout
- layoutAttributesForElementsInRect:
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds;
- 作用:返回值决定了collectionView停止滚动时最终的偏移量(contentOffset)
- 参数:
- proposedContentOffset:原本情况下,collectionView停止滚动时最终的偏移量
- velocity:滚动速率,通过这个参数可以了解滚动的方向
-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity;
具体实现逻辑
以 Cover Flow 布局的实现为例子进行阐述
进行初始化
- (instancetype)init {if (self = [super init]) {}return self;
}
进行布局,并且 collectionView 显示 rect 改变是进行布局刷新
- (void)prepareLayout {[super prepareLayout];// 水平滚动self.scrollDirection = UICollectionViewScrollDirectionHorizontal;// 决定第一张图片所在的位置CGFloat margin = (self.collectionView.frame.size.width - self.itemSize.width) / 2;self.collectionView.contentInset = UIEdgeInsetsMake(0, margin, 0, margin);
}- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {return YES;
} // return YES to cause the collection view to requery the layout for geometry information
该方法是进行自定义的核心,在这里写各种算法以达成想要的效果
该例子中,我们要实现的效果是在滑动的过程中,item 逐渐接近中心,会逐步增大,直到显示最大值,之后不断远离中心点,item 逐步缩小。这一效果类似于三角函数中的正、余弦函数。初步锁定这两个函数,而要选择哪一个?
在滑动的过程中,我们可以得到
self.collectionView.contentOffset.x :这是内容最左侧相对于 collectionView 最左侧的偏移值
attributes.center.x :这是当前 item 的水平中心点,该 x 值是从内容的最左侧算起,直到当前 item 的水平中心点的,全部加起来就是该 item 的水平中心 x。
再者,无论是否滑动,都有一个固定值:
self.collectionView.center.x :位于屏幕水平方向的中心不变,大小不变。
那么,以第一个item 进行分析,self.collectionView.contentOffset.x 与 self.collectionView.contentOffset.x 的差值与屏幕中心 x 相减的绝对值为item 中心与屏幕中心之间的距离
屏幕中心与item 中心相距为0 屏幕中心与item 中心相距40
由此,我们选择余弦函数进行计算。
- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {// 闪屏现象解决参考 :https://blog.csdn.net/u013282507/article/details/53103816//扩大控制范围,防止出现闪屏现象rect.size.width = rect.size.width + KScreenWidth;rect.origin.x = rect.origin.x - KScreenWidth/2;// 让父类布局好样式NSArray *arr = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];for (UICollectionViewLayoutAttributes *attributes in arr) {CGFloat scale;
// scale = 1.0;// collectionView 的 centerXCGFloat centerX = self.collectionView.center.x;CGFloat step = ABS(centerX - (attributes.center.x - self.collectionView.contentOffset.x));NSLog(@"step %@ : attX %@ - offset %@", @(step), @(attributes.center.x), @(self.collectionView.contentOffset.x));scale = fabsf(cosf(step/centerX * M_PI/5));attributes.transform = CGAffineTransformMakeScale(scale, scale);}return arr;
} // return an array layout attributes instances for all the views in the given rect
确保在滚动结束的时候的显示效果,此处确保某一个item 滚动结束时是居中显示的。
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {// 保证滚动结束后视图的显示效果// 计算出最终显示的矩形框CGRect rect;rect.origin.y = 0;rect.origin.x = proposedContentOffset.x;rect.size = self.collectionView.frame.size;// 获得 super 已经计算好的布局的属性NSArray *arr = [super layoutAttributesForElementsInRect:rect];// 计算 collectionView 最中心点的 x 值CGFloat centerX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5;CGFloat minDelta = MAXFLOAT;for (UICollectionViewLayoutAttributes *attrs in arr) {if (ABS(minDelta) > ABS(attrs.center.x - centerX)) {minDelta = attrs.center.x - centerX;}}proposedContentOffset.x += minDelta;return proposedContentOffset;
} // return a point at which to rest after scrolling - for layouts that want snap-to-point scrolling behavior
代码地址:https://download.csdn.net/download/u013410274/10356732
所有的参考链接:
WWDC 2012 Session 笔记 -- 205 Introducing Collection Views
http://www.cnblogs.com/markstray/p/5822262.html
UICollectionViewLayout 继承 UICollectionViewFlowLayout 自定义布局
https://www.cnblogs.com/wangliang2015/p/5388658.html
自定义流水布局(UICollectionViewFlowLayout 的基本使用)
https://www.cnblogs.com/hissia/p/5723629.html
iOS 利用余弦函数实现卡片浏览工具
https://blog.csdn.net/u013282507/article/details/53103816
这篇关于UICollectionView 的研究之二 :自定义 UICollectionViewFlowLayout的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!