UICollectionView
的显示效果几乎全部由UICollectionViewLayout
负责,而真正存储着每一个 Cell 的位置、大小等属性的是UICollectionViewLayoutAttributes
。每个Cell都对应着一个属于自己的UICollectionViewLayoutAttributes
,包含中心点,大小,形状,透明度和层次关系等等。
而 UICollectionViewLayout
正是利用UICollectionViewLayoutAttributes
里存在的信息对每一个Cell进行布局的。
UICollectionViewLayout
-(void)prepareLayout
专门用来准备布局的,在布局之前,它会调用一次,之后只有在调用 shouldInvalidateLayoutForBoundsChange: 方法并返回YES、调用invalidateLayout
方法和 UICollectionView 刷新的时候才会重新调用。因此,我们通常在这个方法中进行一些一次性的设置和计算,如cell中固定的布局属性等以提高性能。切记调用[super prepareLayout];
-(CGSize)collectionViewContentSize
返回collectionView的内容尺寸,相当于scrollView的contentSize,同样的,由于此方法会多次调用,最好在prepareLayout
方法中进行尺寸计算-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
这个方法是返回某个特定区域的布局的属性的数据集合。返回存放的都是UICollectionViewLayoutAttributes
对象的数组。
该方法比较迷惑的是rect
,具体的rect
既不是当前CollectionView
的size
也不是ContentSize
,也不是当前真正的可见范围,而是比可见范围大比ContentSize
小的一块区域(貌似用屏幕的高的倍数设置的区域),用来缓冲布局,比如说现在视线能看到两个item视图,这个rect大约会是可以显示4个item的范围,好用来提前布局看不到的附近的item,这样滚动时就会看着像全部已经布局好了一样!所以为了性能起见,这个方法内不应该返回所有的item的Attributes 而是应该在区域内的Attributes。相当于预先布局上一个或者下一个item
可以参考这篇文章:https://www.intertech.com/Blog/ios-uicollectionview-tutorial-4-custom-layout-with-scrolling-support-for-collection-views/
实现需要做这几步:
创建一个空的可变数组来存放所有的布局属性。
确定index paths
中哪些cells
的frame
完全或部分位于矩形中。这个计算需要你从collection view的数据源中取出你需要显示的数据。然后在循环中调用你实现的layoutAttributesForItemAtIndexPath:
方法为每个index path
创建并配置一个合适的布局属性对象,并将每个对象添加到数组中。
如果布局中包含supplementary views
,计算矩形内可见supplementaryView
的index paths
,在循环中调用你实现的layoutAttributesForSupplementaryViewOfKind:atIndexPath:
,
并且将这些对象加到数组中。通过为 kind 参数传递你选择的不同字符,你可以区分出不同种类的supplementary views
(比如headers和footers)。当需要创建视图时,collection view 会将 kind 字符传回到你的数据源。记住supplementary
和decoration views
的数量和种类完全由布局控制。不会受到 headers 和 footers 的限制。
如果布局包含decoration views
,计算矩形内可见decoration views
的index paths
,再在循环中调用你实现的layoutAttributesForDecorationViewOfKind:atIndexPath:
并且将这些对象加到数组中。
最后返回数组。-(UICollectionViewLayoutAttributes _)layoutAttributesForItemAtIndexPath:
需要创建并返回对应于indexPath的位置的cell的布局属性对象。
比如滚动时候将item背景,frame或者其他属性设置修改,应该在这个方法里实现。通过调用+[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:]
这个方法,获取到cell的Attributes属性然后进行一系列的修改设置-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
最后,当
collection view的
bounds改变时,布局需要告诉
collection view`是否需要重新计算布局。比如需要动态控制 UICollectionViewLayoutAttributes,就需要调用该方法并返回YES。为了提高性能,应该比较视图当前的bounds和新的bounds来确定返回值:- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {CGRect oldBounds = self.collectionView.bounds;if (CGRectGetWidth(newBounds) != CGRectGetWidth(oldBounds)) {return YES;}return NO;}-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
当在拖动的時候,手指抬起时调用。当滑动停止时,希望UICollectionView
滑动到某个位置(如中心位置),而不是任意位置。重载该方法,其中proposedContentOffset
为系统期望滑动到的位置,velocity
为加速度,可以通过这两个参数以及当前所在的位置计算出你希望它滑动到的位置,具体算法根据需求的不同来实现proposedContentOffset:
默认情况下CollectionView停止滚动时最终的偏移量。velocity:
滚动速率,通过这个参数可以了解滚动的方向。返回值决定了collectionView停止滚动时最终的偏移量contentOffset;(最终collectionView停到哪个位置的偏移量)
UICollectionViewLayoutAttributes
UICollectionViewLayout
实际上是通过UICollectionViewLayoutAttributes
类来实现布局的,每一个UICollectionView
内容视图都对应一个 UICollectionViewLayoutAttributes
对象。可以说自定义布局的主要精力都用在设置UICollectionViewLayoutAttributes
上。
属性列表:
方法列表:
因为UICollectionView
的Cell视图、Supplementary视图还是Decoration视图都是通过它们的 attributes(UICollectionViewLayoutAttributes)
来定义的。所以可以定义一个UICollectionViewLayoutAttributes
子类,并设置自定义属性。注意:自定义的属性只能通过UICollectionReusableView
的
- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
方法在CollectionView
布局时来使之生效。
相关问题
- 设置如何每个item充满屏幕,并让其滚动时有一定的间距:
http://stackoverflow.com/questions/29658328/uicollectionview-horizontal-paging-not-centered
参考资料:
ObjC中国 - 自定义Collection View布局
WWDC 2012 Session笔记-UICollectionView
使用 UICollectionView 实现的一个卡片动画(Swift)
布局万花筒:UIColletionview
UICollectionView与iCarousel
为 UICollectionView 设置不同的 Section 背景颜色