概述
在上一篇博客中,简单了解了一下 UICollectionView的构成和使用,在这篇文章中我们来学习一下几种经典的布局,在这之前我们需要来了解一个比较重要的类
UICollectionViewLayoutAttributes
UICollectionViewLayoutAttributes是一个非常重要的类,由他提供给 cell各种属性,看一下其有哪些重要的属性:
-
@property (nonatomic) CGRect frame;
// 大小 -
@property (nonatomic) CGPoint center;
// 中心位置 -
@property (nonatomic) CGSize size;
// 形状 -
@property (nonatomic) CATransform3D transform3D;
// 3D 动画 -
@property (nonatomic) CGRect bounds NS_AVAILABLE_IOS(7_0);
//尺寸 -
@property (nonatomic) CGAffineTransform transform NS_AVAILABLE_IOS(7_0);
// 形变 -
@property (nonatomic) CGFloat alpha;
// 透明度 -
@property (nonatomic, getter=isHidden) BOOL hidden; // As an optimization, UICollectionView might not create a view for items whose hidden attribute is YES
从上面可以看出,其实一个 cell 对应一个UICollectionViewLayoutAttributes对象,这个对象决定了cell 的 fram 等属性
线性布局
从图片来看,图片可以左右滑动,并且停留在屏幕最中间的 cell 会自动放大,这个简单的效果,让我们来看看是如何实现的
首先,我们来考虑用系统的流水布局来实现这个效果,随后发现系统并没有提供相关 API, 接下来需要我们自定义一个线性布局,这个布局继承自系统提供的流水布局
** 用来做布局的初始化操作**
- (void)prepareLayout
{
[super prepareLayout];
// 水平滚动
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
// 设置内边距
CGFloat inset = (self.collectionView.frame.size.width - self.itemSize.width) * 0.5;
self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset);
}
** 返回一个数组,数组内存放着 rect 范围内所有元素的排布**
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
// 获得super已经计算好的布局属性
NSArray *array = [super layoutAttributesForElementsInRect:rect];
// 计算collectionView最中心点的x值
CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.frame.size.width * 0.5;
// 在原有布局属性的基础上,进行微调
for (UICollectionViewLayoutAttributes *attrs in array) {
// cell的中心点x 和 collectionView最中心点的x值 的间距
CGFloat delta = ABS(attrs.center.x - centerX);
// 根据间距值 计算 cell的缩放比例
CGFloat scale = 1 - delta / self.collectionView.frame.size.width;
// 设置缩放比例
attrs.transform = CGAffineTransformMakeScale(scale, scale);
}
return array;
}
返回值决定了 collectionVIew 停止滚动时的偏移量
- (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 *array = [super layoutAttributesForElementsInRect:rect];
// 计算collectionView最中心点的x值
CGFloat centerX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5;
// 存放最小的间距值
CGFloat minDelta = MAXFLOAT;
for (UICollectionViewLayoutAttributes *attrs in array) {
if (ABS(minDelta) > ABS(attrs.center.x - centerX)) {
minDelta = attrs.center.x - centerX;
}
}
// 修改原有的偏移量
proposedContentOffset.x += minDelta;
return proposedContentOffset;
}
** 需要注意的是,当 collectionView的显示范围发生变化时,会调用此方法来决定是否重新刷新 UI**
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return YES;
}```
***
# 商品展示
![商品展示.png](http://upload-images.jianshu.io/upload_images/694552-031730fcd9981e45.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
** 商品展示效果可以上下滑动,图片交错排布,这个布局需要继承自UICollectionViewLayout(其实流水布局就继承自他),因为我们不需要流水布局为我们计算一些布局**
**懒加载一个数组,里面存放着布局属UICollectionViewLayoutAttributes**
- (NSMutableArray *)attrsArray
{
if (!_attrsArray) {
_attrsArray = [NSMutableArray array];
}
return _attrsArray;
}```
** 准备工作,计算好返回的属性**
- (void)prepareLayout
{
[super prepareLayout];
[self.attrsArray removeAllObjects];
NSInteger count = [self.collectionView numberOfItemsInSection:0];
for (int i = 0; i < count; i++) {
// 创建UICollectionViewLayoutAttributes
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
// 设置布局属性
CGFloat width = self.collectionView.frame.size.width * 0.5;
if (i == 0) {
CGFloat height = width;
CGFloat x = 0;
CGFloat y = 0;
attrs.frame = CGRectMake(x, y, width, height);
} else if (i == 1) {
CGFloat height = width * 0.5;
CGFloat x = width;
CGFloat y = 0;
attrs.frame = CGRectMake(x, y, width, height);
} else if (i == 2) {
CGFloat height = width * 0.5;
CGFloat x = width;
CGFloat y = height;
attrs.frame = CGRectMake(x, y, width, height);
} else if (i == 3) {
CGFloat height = width * 0.5;
CGFloat x = 0;
CGFloat y = width;
attrs.frame = CGRectMake(x, y, width, height);
} else if (i == 4) {
CGFloat height = width * 0.5;
CGFloat x = 0;
CGFloat y = width + height;
attrs.frame = CGRectMake(x, y, width, height);
} else if (i == 5) {
CGFloat height = width;
CGFloat x = width;
CGFloat y = width;
attrs.frame = CGRectMake(x, y, width, height);
} else {
UICollectionViewLayoutAttributes *lastAttrs = self.attrsArray[i - 6];
CGRect lastFrame = lastAttrs.frame;
lastFrame.origin.y += 2 * width;
attrs.frame = lastFrame;
}
// 添加UICollectionViewLayoutAttributes
[self.attrsArray addObject:attrs];
}
}
** 显示范围内的属性**
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
return self.attrsArray;
}
** collectionview 的内容大小**
- (CGSize)collectionViewContentSize
{
int count = (int)[self.collectionView numberOfItemsInSection:0];
int rows = (count + 3 - 1) / 3;
CGFloat rowH = self.collectionView.frame.size.width * 0.5;
return CGSizeMake(0, rows * rowH);
}
圆形布局
** 这是一个圆形布局,自定义UICollectionViewLayout实现**
/** 布局属性 */
@property (nonatomic, strong) NSMutableArray *attrsArray;
@end
@implementation XMGCircleLayout
- (NSMutableArray *)attrsArray
{
if (!_attrsArray) {
_attrsArray = [NSMutableArray array];
}
return _attrsArray;
}
- (void)prepareLayout
{
[super prepareLayout];
[self.attrsArray removeAllObjects];
NSInteger count = [self.collectionView numberOfItemsInSection:0];
for (int i = 0; i < count; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
[self.attrsArray addObject:attrs];
}
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
return self.attrsArray;
}
/**
* 这个方法需要返回indexPath位置对应cell的布局属性
*/
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger count = [self.collectionView numberOfItemsInSection:0];
CGFloat radius = 70;
// 圆心的位置
CGFloat oX = self.collectionView.frame.size.width * 0.5;
CGFloat oY = self.collectionView.frame.size.height * 0.5;
UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
attrs.size = CGSizeMake(50, 50);
if (count == 1) {
attrs.center = CGPointMake(oX, oY);
} else {
CGFloat angle = (2 * M_PI / count) * indexPath.item;
CGFloat centerX = oX + radius * sin(angle);
CGFloat centerY = oY + radius * cos(angle);
attrs.center = CGPointMake(centerX, centerY);
}
return attrs;
}