首先是效果演示
特点:可以自由设置瀑布流的总列数(效果演示为2列)
虽然iphone手机的系统相册没有使用这种布局效果,瀑布流依然是一种很常见的布局方式!!!下面来详细介绍如何实现这种布局.
首先使用的类是UICollectionView
我们要做的是自定义UICollectionViewCell和UICollectionViewLayout
1、自定义UICollectionViewCell类,只需要一个UIImageView即可,frame占满整个cell.
2、重点是自定义UICollectionViewLayout,注意一定要继承于UICollectionViewLayout,千万别继承于UIColletionViewFlowLayout.
3、另外还需要计算图片高度.
为什么要自定义UICollectionViewLayout ?
因为我们需要设置每个item的高度以及位置, 注意这里是位置, 我们真的会设置每个item的位置的相信我!!!自定义UICollectionViewLayout必须要重写三个协议方法,后面会讲到.
为什么要计算图片高度 ?
因为图片宽度固定,所以需要按照图片的比例来计算高度,使图片等比例显示.这样的好处是,妈妈再也不用担心我的照片被拉伸的奇形怪状了...而且还需要用图片的高度来计算整个CollectionView的contentSize...打完收工!!!
主菜来了!!!
以下内容均在自定义的CustomCollectionViewLayout类里边
//自定义UICollectionViewLayout必须要重写的三个协议方法 //1.计算每个item的大小和位置 - (void)prepareLayout; //2.返回每个item的布局属性 - (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect; //3.返回collectionView的总高度 - (CGSize)collectionViewContentSize;
可以看到第三个方法使用了Nullability和泛型,系统的方法都添加了iOS 9新特性。
通过上边的三个方法名我们可以大致了解需要去做什么.说一下第二个方法,需要返回一个数组,数组存放布局属性(UICollectionViewLayoutAttributes)对象.那么我们需要写一个属性数组(attributesArray),将布局属性放入这个属性数组并返回.
还需要什么呢 ?看了文章开头的朋友应该注意到了,设置瀑布流的列数当然得有个属性(numberOfColumns)来表示列数.
请注意,因为要在外部设置列数,所以这个属性需要写在自定义类的.h文件中
另外为了方便,定义一个属性(itemWidth)来表示item的宽度
再定义一个属性(contentHeight)来表示整个collectionView的contenView的高度
首先是初始化,并没有什么问题
- (instancetype)init { self = [super init]; if (self) { _attributesArray = [NSMutableArray array]; // 默认值设置为2列 _numberOfColumns = 2; _contentHeight = 0.0f; _cellMargin = 5.0f;/**< 用来表示间距的属性 */ } return self; }
然后是getter方法,只需要使用点语法即可得到itemWidth的值(因为它就是固定的)
- (CGFloat)itemWidth { //所有边距的和.两列时有三个边距, 三列时有四个边距,逻辑强大就是好... CGFloat allMargin = (_numberOfColumns + 1) * _cellMargin; //除去边界之后的总宽度 CGFloat noMarginWidth = CGRectGetWidth(self.collectionView.bounds) - allMargin; //出去边距的总宽度除以列数得到每一列的宽度(也就是itemWidth) return noMarginWidth / _numberOfColumns; }
---接下来是难点---
必须重写的第一个方法
- (void)prepareLayout { // 定义变量记录高度最小的列,初始为第0列高度最小. #pragma mark - 注意这个是从0开始算的啊!!! NSInteger shortestColumn = 0; #pragma mark - 注意这个是从0开始算的啊!!! // 存储每一列的总高度.因为添加图片的列高度会变,所以需要定义一个数组来记录列的总高度. NSMutableArray *columnHeightArray = [NSMutableArray array]; // 设置列的初始高度为边距的高度,没毛病!!! for (int i = 0; i < _numberOfColumns; i++) { // 所有列初始高度均设置为cell的间距 [columnHeightArray addObject:@(_cellMargin)]; } // 遍历collectionView中第 0 区中的所有item for (int i = 0; i < [self.collectionView numberOfItemsInSection:0]; i++) { //需要用到这个玩意,提前拿到. NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0]; // 创建系统需要的布局属性对象,看后边的参数就知道这就是每个item的布局属性了 UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath: indexPath]; // 将布局属性放入数组中,这个数组当然是一开始定义的布局属性数组了 [_attributesArray addObject:layoutAttributes]; // 设置每个item的位置(x, y, width, height) // 横坐标的起始位置 #pragma mark - 比如一共两列,现在要放一张图片上去,需要放到高度最小的那一列. #pragma mark - 假设第0列最短,那么item的x坐标就是从一个边距宽度那里开始. #pragma mark - (itemWidth + cellMargin)为一个整体 CGFloat x = (self.itemWidth + _cellMargin) * shortestColumn + _cellMargin; // 纵坐标就是 总高度数组 中最小列对应的高度 #pragma mark - 图片始终是添加在高度最小的那一列 CGFloat y = [columnHeightArray[column] floatValue];/**<注意类型转换 */ // 宽度没什么好说的 CGFloat width = self.itemWidth; #pragma mark - 这里给自定义的类声明了一个协议,通过协议得到图片的高度,调用时机就是需要item高度的时候 #pragma mark - 将Item的宽度传给代理人(ViewController),VC计算好高度后将高度返回给自定义类 #pragma mark - 也就是↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ CGFloat height = [self.delegate collectionView:self.collectionView layout:self width:self.itemWidth heightForItemAtIndexPath:indexPath]; // 大功告成,设置item的位置信息,没什么好说 layoutAttributes.frame = CGRectMake(x, y, width, height); // 上边废了半天劲放了一个item上去了,总高度数组是不是该更新一下数据了 columnHeightArray[shortestColumn] = @([columnHeightArray[shortestColumn] floatValue] + height + _cellMargin); // 整个内容的高度,通过比较得到较大值作为整个内容的高度 self.contentHeight = MAX(self.contentHeight, [columnHeightArray[shortestColumn] floatValue]); // 刚才放了一个item上去,那么此时此刻哪一列的高度比较低呢 for (int i = 0; i < _numberOfColumns; i++) { //当前列的高度(刚才添加item的那一列) CGFloat currentHeight = [columnHeightArray[shortestColumn] floatValue]; // 取出第i列中存放列高度 CGFloat height = [columnHeightArray[i] floatValue]; if (currentHeight > height) { //第i列高度(height)最低时,高度最低的列(shortestColumn)当然就是第i列了 shortestColumn = i; } } } // 思考下只使用上边的代码会出现什么问题 // 并不能影响心情,请不要恐慌... // 提示:这个方法会被多次调用,数组 }
---难点已经结束---
没有理解的朋友请重点看上边的难点方法.
必须重写的第二个方法
// 2.返回的是, 每个item对应的布局属性 - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect { return _attributesArray; }
必须重写的第三个方法
// 3.返回CollectionView的滚动范围 - (CGSize)collectionViewContentSize { return CGSizeMake(0, _contentHeight); }
ViewController里边关于CollectionView的创建和协议方法就没什么好说的了.
看下自定义类CustomCollectionViewLayout的创建以及属性的赋值情况:
CustomCollectionViewLayout *layout = [[CustomCollectionViewLayout alloc] init]; layout.numberOfColumns = 2;/**< 在ViewController里设置瀑布流的列数,2列或3列为最佳 */ layout.delegate = self;/**< 指定VC为计算高度协议方法的代理人 */
再看下协议方法的实现部分(在ViewController.m中实现)
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout width:(CGFloat)width heightForItemAtIndexPath:(nonnull NSIndexPath *)indexPath { UIImage *image = _imagesArray[indexPath.row]; // 根据传过来的宽度来设置一个合适的矩形, 高度设为CGFLOAT_MAX表示以宽度来计算高度 CGRect boundingRect = CGRectMake(0, 0, width, CGFLOAT_MAX); // 通过系统函数来得到最终的矩形,需要引入头文件 // #import <AVFoundation/AVFoundation.h> CGRect imageCurrentRect = AVMakeRectWithAspectRatioInsideRect(image.size, boundingRect); return imageCurrentRect.size.height; }
总结
到这里呢,在IOS实现瀑布流就算是结束了,有兴趣的朋友可以自己动手试一下,希望本文对大家开发IOS有所帮助。
本文向大家介绍IOS简单实现瀑布流UICollectionView,包括了IOS简单实现瀑布流UICollectionView的使用技巧和注意事项,需要的朋友参考一下 UICollectionView 比tableView 灵活,功能也强大很多。系统实现了流式布局,但用处还有很多限制。 要想实现更灵活的布局,就咬重写UICollectionViewLayout。 先看下实现效果: 废话不多说,直接
本文向大家介绍IOS实现自定义布局瀑布流,包括了IOS实现自定义布局瀑布流的使用技巧和注意事项,需要的朋友参考一下 瀑布流是电商应用展示商品通常采用的一种方式,如图示例 瀑布流的实现方式,通常有以下几种 通过UITableView实现(不常用) 通过UIScrollView实现(工作量较大) 通过UICollectionView实现(通常采用的方式) 一、UICollectionView基础 1、
本文向大家介绍iOS实现水平方向瀑布流,包括了iOS实现水平方向瀑布流的使用技巧和注意事项,需要的朋友参考一下 效果 源码:https://github.com/YouXianMing/Animations 细节 继承UICollectionViewLayout 重载UICollectionViewLayout的四个方法 部分实现细节 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家
本文向大家介绍iOS瀑布流的简单实现(Swift),包括了iOS瀑布流的简单实现(Swift)的使用技巧和注意事项,需要的朋友参考一下 这段时间突然想到一个很久之前用到的知识-瀑布流,本来想用一个简单的方法,发现自己走入了歧途,最终只能狠下心来重写UICollectionViewFlowLayout.下面我将用两种方法实现瀑布流,以及会介绍第一种实现的bug. <1>第一种 效果图如下所示: 这种
本文向大家介绍ios基于UICollectionView实现横向瀑布流,包括了ios基于UICollectionView实现横向瀑布流的使用技巧和注意事项,需要的朋友参考一下 在网上找了许久,一直没有发现有提供横向瀑布流效果的。在项目中用到了我就在垂直瀑布流的基础上,进行了修改,做出了横向瀑布流的效果。同时也对一些UICollectionView的属性进行简单的注释,方便以后查阅。 1、首先要写一
本文向大家介绍JavaScript实现瀑布流以及加载效果,包括了JavaScript实现瀑布流以及加载效果的使用技巧和注意事项,需要的朋友参考一下 一、瀑布流是个啥? 瀑布流,是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。 最早采用瀑布流布局的网站是Pinterest,逐渐在国内流行开来,比如我们熟知的百度图