一、效果展示

二、思路分析

  1> 布局的基本流程

  当设置好collectionView的布局方式之后(UICollectionViewFlowLayout),当系统开始布局的时候,会调用 prepareLayout 来布局  

- (void)prepareLayout;

  与此同时,collectionViewCell 的每个控件的布局属性都会调用 以下方法来设置(可以重写方法来修改每个cell 的数值)

 - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect

  2> 每列 的高度计算方式

    1.计算所有的cell 的高度,然后平均下来,得到itemSize 然后计算每列的高度-----> 该方法不够精确,而且极端情况下会相差太大

    2.每次计算cell 的时候,找出高度最低的那一列,优先添加到该列,然后依次计算------> 该方法误差较小

  Tip: 在collectionView中,contentSize 就是一个摆设,不能根据这个来计算 collectionView 的大小,系统会自动的根据 itemSize 来计算

三、核心代码实现

  

 //
 //  WaterFall.h
 //  瀑布流
 //
 //  Created by gxiangzi on 15/9/16.
 //  Copyright © 2015年 hqu. All rights reserved.
 //

 #import <UIKit/UIKit.h>

 @interface WaterFall : UICollectionViewFlowLayout

 // 计算列数
 @property (assign, nonatomic) NSInteger columCount;
 // 所有的模型数组
 @property (strong, nonatomic) NSArray * dataList;

 @end
 //
 //  WaterFall.m
 //  瀑布流
 //
 //  Created by gxiangzi on 15/9/16.
 //  Copyright © 2015年 hqu. All rights reserved.
 //

 /**

  // 获得高度的办法
     1. 计算总的高度,然后计算每个高度,最后设置itemsize 来计算
     2. 找出最高的列,然后根据最高的列来计算
  注意:collectionView 的contentView 是一个摆设,没有实际效果,需要根据 itemSize 来计算

  */

 #import "WaterFall.h"
 #import "Shop.h"

 @interface WaterFall ()

 @property (nonatomic,strong) NSMutableArray * itemsAttribute;

 @end

 @implementation WaterFall

 -(void)prepareLayout
 {
     [super prepareLayout];

 //    self.sectionFootersPinToVisibleBounds = YES;

     // 计算每列的宽度
     CGFloat contentWidth = self.collectionView.bounds.size.width - self.sectionInset.left - self.sectionInset.right;
         CGFloat colWidth = (contentWidth - (self.columCount - ) * self.minimumInteritemSpacing) / self.columCount;
     self.minimumInteritemSpacing = ;
     self.minimumLineSpacing = ;

     [self attribute:colWidth];
 }

 - (void) attribute:(CGFloat) colWidth
 {
     NSInteger colCount[self.columCount];
     CGFloat colHeight[self.columCount];

     ; i<self.columCount; ++i)
     {
         colHeight[i] = ;
         colCount[i] = ;
     }

     // 定义总item高
     CGFloat totoalItemHeight = ;

     // 定义一个可变数组,来存储 素有的属性值
     NSMutableArray * arrayM = [NSMutableArray arrayWithCapacity:self.dataList.count];

     // 计数
     NSInteger index = ;

     // 遍历数组,计算相关的属性
     for (Shop * shop  in self.dataList)
     {

         // 1> 建立布局属性
         NSIndexPath * indexPath = [NSIndexPath indexPathForRow:index inSection:];
         index ++;
         UICollectionViewLayoutAttributes * attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

         // 2.计算当前的列数
         // 计算是第几列
         NSInteger col = [self shortestCol:colHeight];
         // 计算每一列的个数
         colCount[col]++;

         // 3.计算frame
         CGFloat w = shop.w;
         CGFloat h = shop.h * colWidth / w;
         CGFloat x = self.sectionInset.left + (colWidth + self.minimumInteritemSpacing) * col;
         CGFloat y = colHeight[col] + self.minimumLineSpacing;
         // 累加,计算同一列下一个元素的高度
         colHeight[col] += (h + self.minimumLineSpacing);

         attr.frame = CGRectMake(x, y, colWidth, h);

         // 4.计算总的高度
         totoalItemHeight += h;

         // 5.添加到 itemsAttribute
         [arrayM addObject:attr];
     }

     // 计算出最高的那一列
     NSInteger highestCol = [self highestColL:colHeight];
     // 设置 itemSize,使用总高度的平均值
     self.itemSize = CGSizeMake(colWidth, (colHeight[highestCol]- colCount[highestCol] * self.minimumInteritemSpacing) / colCount[highestCol]);

     // 添加页脚属性

     UICollectionViewLayoutAttributes * footer = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:[NSIndexPath indexPathForRow: inSection:]];
     footer.frame = CGRectMake(, colHeight[highestCol], self.collectionView.bounds.size.width, );

     [arrayM addObject:footer];

     self.itemsAttribute = arrayM;
 }

 /// 返回所有 cell 的属性数组
 - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
 {
     return self.itemsAttribute;
 }

 /// 获得最少的那一列
 ///
 /// @param colHeight 列高数组
 ///
 /// @return 最少的列号
 - (NSInteger) shortestCol:(CGFloat *)colHeight {
     CGFloat min = MAXFLOAT;
     CGFloat col = ;

     ; i<self.columCount; ++i)
     {
         if (colHeight[i] <  min) {
             min = colHeight[i];
             col = i;
         }
     }
     return col;
 }

 /// 获得最高的那一列
 ///
 /// @param colHeight 列高数组
 ///
 /// @return 最高的列号
 - (NSInteger) highestColL:(CGFloat *)colHeight {

     CGFloat max = ;
     CGFloat col = ;

     ; i<self.columCount; ++i)
     {
         if(colHeight[i] > max)
         {
             max = colHeight[i];
             col = i;
         }
     }

     return col;
 }

 #pragma mark - 懒加载 属性数组
 - (NSMutableArray *)itemsAttribute
 {
     if (_itemsAttribute == nil)
     {
         _itemsAttribute = [NSMutableArray array];
     }
     return _itemsAttribute;
 }

 @end

【iOS开发】collectionView 瀑布流实现的更多相关文章

  1. IOS开发之瀑布流照片墙实现

    想必大家已经对互联网传统的照片布局方式司空见惯了,这种行列分明的布局虽然对用户来说简洁明了,但是长久的使用难免会产生审美疲劳.现在网上流行一种叫做“瀑布流”的照片布局样式,这种行与列参差不齐的状态着实 ...

  2. iOS之简单瀑布流的实现

    iOS之简单瀑布流的实现   前言 超简单的瀑布流实现,这里说一下笔者的思路,详细代码在这里. 实现思路 collectionView能实现各中吊炸天的布局,其精髓就在于UICollectionVie ...

  3. iOS开发 - AVPlayer实现流音频边播边存

    边播边下有三套左右实现思路,本文使用AVPlayer + AVURLAsset实现. 概述 1. AVPlayer简介 AVPlayer存在于AVFoundation中,可以播放视频和音频,可以理解为 ...

  4. iOS UITabView简写瀑布流

    代码demo 一.tabViewCell,通过image的比例高算出cell 的高度 #import "TableViewCell.h" @implementation Table ...

  5. iOS开发之窥探UICollectionViewController(四) --一款功能强大的自定义瀑布流

    在上一篇博客中<iOS开发之窥探UICollectionViewController(三) --使用UICollectionView自定义瀑布流>,自定义瀑布流的列数,Cell的外边距,C ...

  6. iOS开发之窥探UICollectionViewController(三) --使用UICollectionView自定义瀑布流

    上篇博客的实例是自带的UICollectionViewDelegateFlowLayout布局基础上来做的Demo, 详情请看<iOS开发之窥探UICollectionViewControlle ...

  7. iOS开发笔记15:地图坐标转换那些事、block引用循环/weak–strong dance、UICollectionviewLayout及瀑布流、图层混合

    1.地图坐标转换那些事 (1)投影坐标系与地理坐标系 地理坐标系使用三维球面来定义地球上的位置,单位即经纬度.但经纬度无法精确测量距离戒面积,也难以在平面地图戒计算机屏幕上显示数据.通过投影的方式可以 ...

  8. iOS开发:代码通用性以及其规范 第一篇(附带,自定义UITextView\进度条\双表显示\瀑布流 代码设计思路)

    在iOS团队开发中,我见过一些人的代码,也修改过他们的代码.有的人的代码写的非常之规范.通用,几乎不用交流,就可以知道如何修改以及在它基础上扩展延生.有的人的代码写的很垃圾,一眼看过去,简直会怀疑自己 ...

  9. iOS开发-UICollectionView实现瀑布流

    关于瀑布流的实现网上有很多种解法,自定义控件,TableView+ScrollView,UICollectionView是iOS6发布之后用于展示集合视图,算起来已经发布三年左右了,不过知识点是不变的 ...

随机推荐

  1. 执行yiic webapp命令时报错:php.exe不是内部或外部命令,也不是可运行的程序

    在执行 yiic webapp ../abc 命令时报错: “php.exe”不是内部或外部命令,也不是可运行的程序 或批处理文件. 这是因为yiic批处理程序找不到php.exe的执行路径引起的. ...

  2. sgu To xor or not to xor

    题意:从n个数中,选择一些数,使得异或最大. #include <cstdio> #include <cstring> #include <algorithm> # ...

  3. LeetCode_Subsets

    Given a set of distinct integers, S, return all possible subsets. Note: Elements in a subset must be ...

  4. UML--一些图

    通过UML来表示汽车,简洁明了. 统一建模语言--UML. 参与者Actor,参与者代表了现实世界的人.人. 用例use case,就是参与者要做什么并且获得什么.事. 业务场景,用例场景.规则. 业 ...

  5. jquery使用总结

    jquery使用总结-常用DOM操作 (1)查询或设置元素属性操作 html()   //获取匹配元素集合中的第1个元素 html(htmlString)  //为匹配集合中的所有元素设置内容 tex ...

  6. 04747_Java语言程序设计(一)_第8章_多线程

    例8.1应用程序用Thread子类实现多线程. import java.util.Date; public class Example8_1 { static Athread threadA; sta ...

  7. 跨域资源共享 CORS 详解

    CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing). 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从 ...

  8. Oracle 学习笔记 19 -- 触发器和包浅析(PL/SQL)

    触发器是存放在数据库中的一种特殊类型的子程序.不能被用户直接调用,而是当特定事件或操作发生时由系统自己主动 调用执行.触发器不能接受參数.所以执行触发器就叫做触发或点火.Oracle事件指的是数据库的 ...

  9. Android系统默认Home应用程序(Launcher)的启动过程源代码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个 Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home ...

  10. PHP <<EOF EOF的使用方法

    PHP <<EOF EOF的使用方法 <?php     $name = '浅水游';     print <<<EOT             <html& ...