• collectionView布局原理及瀑布流布局方式--备用


    最近学习到了瀑布流的实现方法,瀑布流的实现方式有多种,这里应用collectionView来重写其UICollectionViewLayout进行布局是最为简单方便的。但再用其布局之前必须了解其布局原理。  

    在这里笔者挑出其中较为重要的几个方法来进行讲解。

    1.- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds

    当collectionView视图位置有新改变(发生移动)时调用,其若返回YES则重新布局

    2.- (void)prepareLayout

    准备好布局时调用。此时collectionView所有属性都已确定。读者在这里可以将collectionView当做画布,有了画布后,我们便可以在其上面画出每个item

    3.- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect

    返回collectionView视图中所有视图的属性(UICollectionViewLayoutAttributes)数组

    4.- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath

    返回indexPath对应item的属性

    5.- (CGSize)collectionViewContentSize

    设置collectionView的可显示范围

    这些方法中最重要的便是3,4方法,

    在3方法中返回所有视图属性数组,并根据这些属性进行布局,

    而4方法则返回每个item的属性,我们则在这里设置每个item的属性(主要是frame),就可以让collectionView按照我们的意愿进行布局了!

    (在这里我们不需要用到1方法,若item属性根据滑动改变,此时就需要随时进行布局改变)  

    瀑布流的实现示意图如下

    由图示意可看出除开最开始3个item,后面的item都是存放3列中的最短列上面,因此我们只需要计算出每个item的frame,并摆放的话那我们的瀑布流自然就成功了。

    .h文件中

    1 typedef CGFloat(^HeightBlock)(NSIndexPath *indexPath , CGFloat width);

    2 @interface BQWaterLayout : UICollectionViewLayout

    3 /** 列数 */

    4 @property (nonatomic, assign) NSInteger lineNumber;

    5 /** 行间距 */

    6 @property (nonatomic, assign) CGFloat rowSpacing;

    7 /** 列间距 */

    8 @property (nonatomic, assign) CGFloat lineSpacing;

    9 /** 内边距 */

    10 @property (nonatomic, assign) UIEdgeInsets sectionInset;

    11 /**

    12 * 计算各个item高度方法 必须实现

    13 *

    14 * @param block 设计计算item高度的block

    15 */

    16 - (void)computeIndexCellHeightWithWidthBlock:(CGFloat(^)(NSIndexPath *indexPath , CGFloat width))block;

    17 @end

    为了方便修改瀑布流的布局我们需要设置排列布局的各个接口,因为瀑布流中只能通过列数计算出item的宽,因此需要使用computeIndexCellHeightWithWidthBlock来计算出每个item的高度(利用宽高比)!

    .m文件中

    代码中注释已经写的很明白了,无需多做解释,此处写法只能实现item的布局,不能添加headview或footview!

    1 @interface BQWaterLayout()

    2 /** 存放每列高度字典*/

    3 @property (nonatomic, strong) NSMutableDictionary *dicOfheight;

    4 /** 存放所有item的attrubutes属性*/

    5 @property (nonatomic, strong) NSMutableArray *array;

    6 /** 计算每个item高度的block,必须实现*/

    7 @property (nonatomic, copy) HeightBlock block;

    8 @end

    9

    10 @implementation BQWaterLayout

    11 - (instancetype)init

    12 {

    13 self = [super init];

    14 if (self) {

    15 //对默认属性进行设置

    16 /**

    17 默认行数 3行

    18 默认行间距 10.0f

    19 默认列间距 10.0f

    20 默认内边距 top:10 left:10 bottom:10 right:10

    21 */

    22 self.lineNumber = 3;

    23 self.rowSpacing = 10.0f;

    24 self.lineSpacing = 10.0f;

    25 self.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);

    26 _dicOfheight = [NSMutableDictionary dictionary];

    27 _array = [NSMutableArray array];

    28 }

    29 return self;

    30 }

    31

    32 /**

    33 * 准备好布局时调用

    34 */

    35 - (void)prepareLayout{

    36 [super prepareLayout];

    37 NSInteger count = [self.collectionView numberOfItemsInSection:0];

    38 //初始化好每列的高度

    39 for (NSInteger i = 0; i < self.lineNumber ; i++) {

    40 [_dicOfheight setObject:@(self.sectionInset.top) forKey:[NSString stringWithFormat:@"%ld",i]];

    41 }

    42 //得到每个item的属性值进行存储

    43 for (NSInteger i = 0 ; i < count; i ++) {

    44 NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];

    45 [_array addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];

    46 }

    47 }

    48 /**

    49 * 设置可滚动区域范围

    50 */

    51 - (CGSize)collectionViewContentSize{

    52 NSLog(@"collectionViewContentSize");

    53 __block NSString *maxHeightline = @"0";

    54 [_dicOfheight enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSNumber *obj, BOOL *stop) {

    55 if ([_dicOfheight[maxHeightline] floatValue] < [obj floatValue] ) {

    56 maxHeightline = key;

    57 }

    58 }];

    59 return CGSizeMake(self.collectionView.bounds.size.width, [_dicOfheight[maxHeightline] floatValue] + self.sectionInset.bottom);

    60 }

    61 /**

    62 * 计算indexPath下item的属性的方法

    63 *

    64 * @return item的属性

    65 */

    66 - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{

    67 //通过indexPath创建一个item属性attr

    68 UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

    69 //计算item宽

    70 CGFloat itemW = (self.collectionView.bounds.size.width - (self.sectionInset.left + self.sectionInset.right) - (self.lineNumber - 1) * self.lineSpacing) / self.lineNumber;

    71 CGFloat itemH;

    72 //计算item高

    73 if (self.block != nil) {

    74 itemH = self.block(indexPath, itemW);

    75 }else{

    76 NSAssert(itemH != 0,@"Please implement computeIndexCellHeightWithWidthBlock Method");

    77 }

    78 //计算item的frame

    79 CGRect frame;

    80 frame.size = CGSizeMake(itemW, itemH);

    81 //循环遍历找出高度最短行

    82 __block NSString *lineMinHeight = @"0";

    83 [_dicOfheight enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSNumber *obj, BOOL *stop) {

    84 if ([_dicOfheight[lineMinHeight] floatValue] > [obj floatValue]) {

    85 lineMinHeight = key;

    86 }

    87 }];

    88 int line = [lineMinHeight intValue];

    89 //找出最短行后,计算item位置

    90 frame.origin = CGPointMake(self.sectionInset.left + line * (itemW + self.lineSpacing), [_dicOfheight[lineMinHeight] floatValue]);

    91 _dicOfheight[lineMinHeight] = @(frame.size.height + self.rowSpacing + [_dicOfheight[lineMinHeight] floatValue]);

    92 attr.frame = frame;

    93

    94 return attr;

    95 }

    96 /**

    97 * 返回视图框内item的属性,可以直接返回所有item属性

    98 */

    99 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{

    100 return _array;

    101 }

    102 /**

    103 * 设置计算高度block方法

    104 *

    105 * @param block 计算item高度的block

    106 */

    107 - (void)computeIndexCellHeightWithWidthBlock:(CGFloat (^)(NSIndexPath *, CGFloat))block{

    108 if (self.block != block) {

    109 self.block = block;

    110 }

    111 }

    112 @end

    至此一个简单的collectionViewLayout瀑布流布局便设置完成,只需要在自己代码中使用此布局便可以得到一个瀑布流了!

    笔者本来开始还担心如果item过多,那么设置的属性就会过多,比如数组内存放一千或一万个item的属性,后来经过笔者测试后发现,系统应该每次都是事先计算好了所有item的属性(通过tableView计算每行高度的代理方法来思考),因此直接初始化好所有item的属性做法应该不会有太大弊端!

  • 相关阅读:
    CTFHUB-技能树-Web-文件上传
    CTFHUB-技能树-Web-XSS-反射型
    一个算法习题
    python2和python3如何共存并都可用
    beacon帧字段结构最全总结(一)——beacon基本结构
    WiFi基础知识整理
    无线 WIFI 的13个信道频率范围
    Wifi模块的工作原理
    WIFI无线协议802.11a/b/g/n/ac的演变以及区别
    HTTP网络通讯协议详解
  • 原文地址:https://www.cnblogs.com/isItOk/p/4875968.html
Copyright © 2020-2023  润新知