已更新Demo(2017-8-8):
https://pan.baidu.com/s/1bDXrYI
不放github了
下面这种需求应该是会经常遇到的:
需求:固定高度一个区域,里面左右分页显示很多个图标,在每一页中的图标先从左往右排,排满后再从上往下排。这一页排满后排下一页。
图中这样的:上面cell的顺序我已经标出来了。
p1.png
像这样的需求,第一反应是用UICollectionView来写,用UICollectionViewFlowLayout,然后设置为横向的。但是,采用这种方式来写,出来的效果就会是这样的:
p2.png
那么还可以怎么实现?可以用大的cell包5个小图标在里面。这样实现按说没问题,但是如果单个图标这里复杂一点,就需要调很久了,而且如果要根据屏幕尺寸来定每行显示的图标个数也很麻烦。
下面是我的实现:
子类化 UICollectionViewFlowLayout
DDCollectionViewHorizontalLayout.h
@interface DDCollectionViewHorizontalLayout : UICollectionViewFlowLayout
// 一行中 cell的个数
@property (nonatomic) NSUInteger itemCountPerRow;
// 一页显示多少行
@property (nonatomic) NSUInteger rowCount;
@end
DDCollectionViewHorizontalLayout.m
@interface DDCollectionViewHorizontalLayout ()
@property (strong, nonatomic) NSMutableArray *allAttributes;
@end
- (void)prepareLayout
{
[super prepareLayout];
self.allAttributes = [NSMutableArray array];
NSUInteger count = [self.collectionView numberOfItemsInSection:0];
for (NSUInteger i = 0; i<count; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
[self.allAttributes addObject:attributes];
}
}
- (CGSize)collectionViewContentSize
{
return [super collectionViewContentSize];
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger item = indexPath.item;
NSUInteger x;
NSUInteger y;
[self targetPositionWithItem:item resultX:&x resultY:&y];
NSUInteger item2 = [self originItemAtX:x y:y];
NSIndexPath *theNewIndexPath = [NSIndexPath indexPathForItem:item2 inSection:indexPath.section];
UICollectionViewLayoutAttributes *theNewAttr = [super layoutAttributesForItemAtIndexPath:theNewIndexPath];
theNewAttr.indexPath = indexPath;
return theNewAttr;
}
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
NSMutableArray *tmp = [NSMutableArray array];
for (UICollectionViewLayoutAttributes *attr in attributes) {
for (UICollectionViewLayoutAttributes *attr2 in self.allAttributes) {
if (attr.indexPath.item == attr2.indexPath.item) {
[tmp addObject:attr2];
break;
}
}
}
return tmp;
}
// 根据 item 计算目标item的位置
// x 横向偏移 y 竖向偏移
- (void)targetPositionWithItem:(NSUInteger)item
resultX:(NSUInteger *)x
resultY:(NSUInteger *)y
{
NSUInteger page = item/(self.itemCountPerRow*self.rowCount);
NSUInteger theX = item % self.itemCountPerRow + page * self.itemCountPerRow;
NSUInteger theY = item / self.itemCountPerRow - page * self.rowCount;
if (x != NULL) {
*x = theX;
}
if (y != NULL) {
*y = theY;
}
}
// 根据偏移量计算item
- (NSUInteger)originItemAtX:(NSUInteger)x
y:(NSUInteger)y
{
NSUInteger item = x * self.rowCount + y;
return item;
}
@end
需要注意的
我这个简单的实现只支持1个section,而且section的items个数必须是 itemCountPerRow * rowCount 的整数倍。这里需要在UICollectionView的代理里面做好数组越界检查,防止数组越界造成崩溃。
完整的实现以后再说吧,现在这样已经满足需求了。
网友评论
- (CGSize)collectionViewContentSize
{
NSUInteger count = [self.collectionView numberOfItemsInSection:0];
NSUInteger perPage = self.rowCount * self.itemCountPerRow;
NSUInteger time = count / perPage;
CGSize size = self.collectionView.frame.size;
size.width = size.width * (time + 1);
return size;
}
其实就是设置ContentSize
http://www.jianshu.com/p/35349e50b637