简介瀑布流的实现

思路

  • 瀑布流核心思想是,每张图片都是从高度最低的一列去拼接,同时图片的尺寸取决于需求,而不是统一的尺寸,进而形成参差不齐的效果
  • 通过UICollectionView的自定义UICollectionViewLayout(布局)去实现
  • 相比用ScrollerView去实现瀑布流,UICollectionView更为简单,因为它已经具备循环利用的功能,如果使用ScrollerView还需要自己去实现循环利用

步骤

  • 新建一个UICollectionViewWaterLayout,继承UICollectionViewLayout
  • 设置代理UICollectionViewWaterLayoutDelegate,用于获取每个item的高度、列数、列间距、行间距等,其中item的高度是必须的,外界通过内部提供的宽度,按比例计算出高度。之所以用代理的方式去获取这些属性,而不是直接用property属性,是因为代理什么时候调用是内部决定的,当用property属性去获取这些属性时,外界可以在任何时候去改变这些属性,这就意味着内部要随时针对这种改变去做响应处理(重写set方法)比较麻烦。
protocol UICollectionViewWaterLayoutDelegate <NSObject>
  @required
  // 返回item的高度(根据width按比例去计算)
  - (CGFloat)waterLayout:(UICollectionViewWaterLayout *)waterLayout heightForItemAtIndexPath:(NSIndexPath *)indexPath andWidth:(CGFloat)width;
  @optional
  // 返回列数
  - (NSInteger)numberOfColumnsInWaterLayout:(UICollectionViewWaterLayout *)waterLayout;
  // 返回行间距
  - (CGFloat)rowMarginForWaterLayout:(UICollectionViewWaterLayout *)waterLayout;
  // 返回列间距
  - (CGFloat)columnMarginForWaterLayout:(UICollectionViewWaterLayout *)waterLayout;
  // 返回内边距
  - (UIEdgeInsets)edgeInsetsForWaterLayout:(UICollectionViewWaterLayout *)waterLayout;
@end
  • 添加成员变量(.h)
 /** 缓存每个item的UICollectionViewLayoutAttributes属性,避免重复计算 */
property (nonatomic, strong) NSMutableArray *attributesArr;
/** 记录每列的高度 */
property (nonatomic, strong) NSMutableArray *columnHeights;
  • 重写prepareLayout方法去初始化布局,注意要先调用父类同名方法
  - (void)prepareLayout { 
    // 要先调用父类的prepareLayout 
    [super prepareLayout]; 
    // 清空原来缓存的列的高度和cell的未知属性,否则当重新调用prepareLayout,会累加 
    [self.attributesArr removeAllObjects]; 
    [self.columnHeights removeAllObjects]; 
    // 设置列高度初始值为顶部内边距 
    for (int i = 0; i < layoutColumn; i++) {
       [self.columnHeights addObject:[NSNumber numberWithDouble:layoutInsets.top]];
     } 
    // 设置每个item的布局属性,并缓存 
    NSInteger itemsTotal = [self.collectionView numberOfItemsInSection:0];
    for (int i = 0; i < itemsTotal; i++) { 
      NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
      UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; 
      attributes.frame = [self getItemFrameWithIndexPath:indexPath]; [self.attributesArr addObject:attributes]; 
    }
}
  • 瀑布流核心计算方法,之所以自己定义一个方法去计算,而不是直接在layoutAttributesForItemAtIndexPath:实现计算方法,并调用它来获取每个item的位置属性,是因为layoutAttributesForItemAtIndexPath:会被系统重复调用,导致列的高度计算错误
  /** 每个item都是放到当前高度最低的列下面 
    * 要计算x,y,就要找到高度最低的列 
    * 宽度 = (collectionView宽度 - 左内边距 - 右内边距 - (列数 - 1)*列间距)/列数 * 高度通过代理获取 
    * x = 左内边距 + 列号 * (宽度 + 列间距) 
    * y = 列的高度 + 行间距 
    */
   - (CGRect)getItemFrameWithIndexPath:(NSIndexPath *)indexPath { 
    // 高度最低的列 
    CGFloat minHeight = [self.columnHeights[0] doubleValue]; 
    NSInteger minHeightCol = 0;
    for (int i = 1; i < self.columnHeights.count; i++) { 
      CGFloat columnHeight = [self.columnHeights[i] doubleValue]; 
      if (minHeight > columnHeight) { 
        minHeight = columnHeight; minHeightCol = i; 
      } 
    }  
    // 计算item的位置 
    CGFloat collectionViewW = self.collectionView.frame.size.width;
    CGFloat itemW = (collectionViewW - self.insets.left - self.insets.right - (self.columns - 1)*self.columnMargin) / self.columns; 
    // 通过使用者实现的代理拿到item的高度
    CGFloat itemH = [self.delegate waterLayout:self heightForItemAtIndexPath:indexPath andWidth:itemW]; 
    CGFloat itemX = (itemW + self.columnMargin) * minHeightCol; 
    CGFloat itemY = minHeight + self.rowMargin; CGRect frame = CGRectMake(itemX, itemY, itemW, itemH);  
   // 更新列的高度 
    self.columnHeights[minHeightCol] = [NSNumber numberWithDouble:CGRectGetMaxY(frame)];  
    return frame;
}
  • 重写layoutAttributesForElementsInRect:,告诉collectionView每个item以什么形式排布,这个方法会被反复调用,所以不适合将计算过程写在这个里面,而是直接用上面计算好的缓存数据
  - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect { 
    return self.attributesArr;
  }
  • 重写layoutAttributesForItemAtIndexPath:方法,如果不重写,UICollectionView在调用切换布局方式的方法时会崩溃
  - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath { 
    return self.attributesArr[indexPath.item];
  }
  • 重写collectionViewContentSize,否则UICollectionView无法滚动,返回的就是滚动范围
  - (CGSize)collectionViewContentSize { 
    // 找到高度最高的列 
    // 滚动高度 = 高度最高的列的高度 + 底部内边距 
    // 记录最低高度
    CGFloat maxHeight = [self.columnHeights[0] doubleValue];
    for(int i = 1; i < self.columnHeights.count; i++) { 
      CGFloat columnHeight = [self.columnHeights[i] doubleValue]; 
      if (maxHeight < columnHeight) { 
        maxHeight = columnHeight;
      }
    }
   return CGSizeMake(0, maxHeight + layoutInsets.bottom);
  }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容