在平时使用的app中会经常碰到一些规格选择,筛选,标签等页面,这些页面的布局展示通常是左对齐流水布局。
实现类似这样的左对齐流水布局有多种方式,如果选项少的话可以直接用UIButton实现。现在我们有一种比较简单的方式可以实现这个目的。
就是对UICollectionView稍加改动,就能轻松实现。
下面介绍一下具体实现的方法。
通过 ZFFlowLayout类可以创建一个默认距离的布局实例,也可以创建一个自定义距离的布局实例。
#import <UIKit/UICollectionViewFlowLayout.h> #import "ZFFlowLayoutMacro.h" #import "ZFFlowLayoutProtocol.h" //流水布局类型 typedef enum : NSUInteger { FlowLayoutType_leftAlign, FlowLayoutType_rightAlign, } FlowLayoutType; @interface ZFFlowLayout : NSObject /*! * @author zhoufei * * @brief 根据传入不同的流失布局类型获取不同的布局实例 * @param flowLayoutType 流水布局类型 * @return 布局实例 */ + (UICollectionViewFlowLayout *)flowLayoutWithFlowLayoutType:(FlowLayoutType)flowLayoutType; /*! * @author zhoufei * * @brief 自定义布局实例:根据传入不同的流失布局类型,item距离四周距离,section距离四周距离 自定义布局实例 * @param flowLayoutType 流水布局类型 * @param itemEdgeInsets 第一个item距离四周的距离 * @param sectionEdgeInsets section距离四周的距离 * @return 布局实例 */ + (UICollectionViewFlowLayout<ZFFlowLayoutProtocol> *)flowLayoutWithFlowLayoutType:(FlowLayoutType)flowLayoutType ItemEdgeInsets:(FlowLayoutItemEdgeInsets)itemEdgeInsets sectionEdgeInsets:(FlowLayoutSectionEdgeInsets)sectionEdgeInsets;
调用如下方法可以根据想要创建的布局类型,生成一个布局实现。
+ (UICollectionViewFlowLayout *)flowLayoutWithFlowLayoutType:(FlowLayoutType)flowLayoutType;
调用如下方法可以根据想要创建的布局类型和第一个item距离四周的距离与section距离四周的距离,生成一个自定义的布局实现。
+ (UICollectionViewFlowLayout<ZFFlowLayoutProtocol> *)flowLayoutWithFlowLayoutType:(FlowLayoutType)flowLayoutType
ItemEdgeInsets:(FlowLayoutItemEdgeInsets)itemEdgeInsets
sectionEdgeInsets:(FlowLayoutSectionEdgeInsets)sectionEdgeInsets;
在第二个方法中使用到了自定义的枚举和结构体,它们的具体实现如下:
#ifndef ZFFlowLayoutMacro_h #define ZFFlowLayoutMacro_h /*!** 左对齐布局时:左上角第一个item 距离四周的距离 右对齐布局时:右上角第一个item 距离四周的距离 ***/ typedef struct FlowLayoutItemEdgeInsets { CGFloat top, left, bottom, right; // specify amount to inset (positive) for each of the edges. values can be negative to 'outset' } FlowLayoutItemEdgeInsets; /*!** item所属的组section 距离四周的距离 ***/ typedef struct FlowLayoutSectionEdgeInsets { CGFloat top, left, bottom, right; // specify amount to inset (positive) for each of the edges. values can be negative to 'outset' } FlowLayoutSectionEdgeInsets; #endif /* ZFFlowLayoutMacro_h */
结构体中值得具体含义已经在注释中写出,这里就不在讲了。
下面讲一下最核心的类 LeftAlignedFlowLayout类
因为这个类代码略有点长,这里就这贴出主要的逻辑代码:
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect { NSMutableArray* attributes = [[super layoutAttributesForElementsInRect:rect] mutableCopy]; NSMutableArray * subArray = [LayoutAttributeTools groupTheSameLineItems:attributes]; [self leftAlign_updateItemAttributeInSigleLine:subArray]; return attributes; } /*! * @author zhoufei * * @brief 更新每个元素的位置 * @param groupArray 归并后的结果数组 */ - (void)leftAlign_updateItemAttributeInSigleLine:(NSMutableArray * )groupArray{ NSMutableArray * modelArray = [NSMutableArray array]; for (NSArray * array in groupArray) { NSInteger count = array.count; if (!count) { continue; } for (int i = 0; i<count; i++) { UICollectionViewLayoutAttributes *attrOne = array[i]; [modelArray addObject:[Attribute AttributeWithIndex:i attrOne.size.width]]; } CGFloat leftWith = 0; for (int i=0; i<count; i++) { UICollectionViewLayoutAttributes *attr = [array objectAtIndex:i]; NSPredicate *predice =[NSPredicate predicateWithFormat:@"index < %d",i]; NSArray * resultArray = [modelArray filteredArrayUsingPredicate:predice]; NSNumber * number = [resultArray valueForKeyPath:@"@sum.width"]; leftWith = self.leftMargin+self.itemMargin*i+number.doubleValue; CGRect frame = attr.frame; frame.origin.x = leftWith; attr.frame = frame; } [modelArray removeAllObjects]; } }
上面这个方法- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
是父类 UICollectionViewFlowLayout的代理方法,在这个方法中可以拿到当前屏幕中显示的所有 UICollectionViewCell的布局实现,我们对
UICollectionViewCell的布局修改也就是在这个方法中。
首先通过方法 [LayoutAttributeTools groupTheSameLineItems:attributes];对屏幕中显示的每一行 UICollectionViewCell 进行分组。这样分组之后逻辑比较清晰。只需要设置每一行UICollectionViewCell的新布局实例,剩余的也都是有每一行组成的。直接来个循环就搞定了。
方法 [self leftAlign_updateItemAttributeInSigleLine:subArray];就是对分组后的UICollectionViewCell进行逐行更新布局实例对象的值。具体实现已经在代码中给出了。
Demo地址:https://github.com/zhfei/ZFFlowLayout
欢迎star。
如果发现不对的地方欢迎批评和指正。