其实对于瀑布流的实现原理并不是多难,像常见的应用一般都是把屏幕分为等宽的两列或三列,在将图片放入每一列,不同的图片判断出不同的高度,在加入列之前,判断哪一列的高度最低,将图片插在那一列的下面。
以前经常使用的是用UITableView或UIScrollView来实现瀑布流
使用UITableView的原理:首先拿到数据,如果要做成3列,就用三个UItableview,宽度平均,高度动态,页面高度取UItableview中最高的;三个UItableview初始化的时候用到tag,然后 showsVerticalScrollIndicator和scrollEnabled设为no,separatorStyle设为 UITableViewCellSeparatorStyleNone,添加到UIview中。
使用UIScrollView的原理:核心是Cell的重用机制,根据图片的大小和相关信息,来判断图片所在位置,和图片高度;下面就用UIScrollView来实现瀑布流:
首先要对UIScrollView子类化,自行绘制UIScrollView视图上的布局,只要思路就是在scrollview上面放三个UIView代表每一个列,然后在每个UIview上添加图片,每次都是挑最短的UIView把图片添加上去
我们创建imageWaterView为UIScrollView的拓展类,SelfimageView为三列上的图片视图,方便管理
通常我们通过网路提取图片,此处模拟网络请求写个json文件创建个Model
Model简单实现为
@interface ImageInfo : NSObject
@property float width;
@property float height;
@property (nonatomic,strong)NSString *thumbURL;
-(id)initWithDictionary:(NSDictionary*)dictionary;
@end
json中也是简单的写点图片信息
{
"thumbURL": "http://amuse.nen.com.cn/imagelist/11/21/9as70n3ir61b.jpg",
"width": 482,
"height": 480
},
另外在图片视图中还利用了第三方类SDWebImage来读取网络图片
#import <UIKit/UIKit.h>
#import "ImageInfo.h"
@interface SelfImageVIew : UIView
@property (nonatomic,strong)ImageInfo *data;
-(id)initWithData:(ImageInfo *)data yPoint:(float) y;
提取Model中的图片信息,得到图片的高宽比来创建图片的缩略图
-(id)initWithData:(ImageInfo *)data yPoint:(float) y
{
float imgW=data.width;//图片原宽度
float imgH=data.height;//图片原高度
float sImgW = WIDTH-4;//缩略图宽带
float sImgH = sImgW*imgH/imgW;//缩略图高度
self = [super initWithFrame:CGRectMake(0, y, WIDTH, sImgH+4)];
if (self) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(2, 2, sImgW, sImgH)];
[imageView setImageWithURL:[NSURL URLWithString:data.thumbURL ]];
[self addSubview:imageView];
}
return self;
}
接下来实现
UIScrollView的创建,先声明我们所需要的变量,创建三列的View,并记录下每一列的高度,记下的高度可以判断下一张图片将插入到那一列下面,另声明出一个方法用来在创建是初始化
#import <UIKit/UIKit.h>
#import "SelfImageVIew.h"
#define SPACE 4
#define WIDTH [UIScreen mainScreen].applicationFrame.size.width/3
@interface ImageWaterView : UIScrollView<ImageDelegate>
{
//第一列,第二列,第三列
UIView *firstView;
UIView *secondView;
UIView *thridView;
//最高列,最低列,行数
int higher,lower,row;
//最高列高度
float highValue;
//记录多少图片
int countImage;
}
//图像对象数组
@property (nonatomic,strong)NSArray *arrayImage;
//初始化瀑布流,array图片对象数组
-(id)initWithDataArray:(NSArray*)array withFrame:(CGRect)rect;
初始化中higher,row,highValue,lower都记录下所需的对应列最高列higher最低列lower最高值highValue,循环将数据中的图片通过
addViews
:
with
:方法添加到图片视图上
-(id)initWithDataArray:(NSArray*)array withFrame:(CGRect)rect
{
self = [super initWithFrame:rect];
if (self) {
self.arrayImage = array;
//初始化参数
[self initParameter];
}
return self;
}
-(void)initParameter
{
//每一列的视图初始化
firstView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, WIDTH, 0)];
secondView = [[UIView alloc]initWithFrame:CGRectMake(WIDTH, 0, WIDTH, 0)];
thridView = [[UIView alloc]initWithFrame:CGRectMake(WIDTH*2, 0, WIDTH, 0)];
higher = row = highValue = lower = 1;
countImage = 0;
for (int i = 0; i<self.arrayImage.count; i++) {
//0%3=0,0-2除3也不可能大于0
if (i/3>0 && i%3==0) {
row++;
}
ImageInfo *data = (ImageInfo*)[self.arrayImage objectAtIndex:i];
countImage ++;
//添加视图
[self addViews:data with:countImage];
//重新设置最高和最低view
[self setHigherAndLower];
}
[self setContentSize:CGSizeMake(WIDTH, highValue)];
[self addSubview:firstView];
[self addSubview:secondView];
[self addSubview:thridView];
}
下面的方法就是在添加图片的时候,刷新最高和最低的View,方便加载和重用的判断
-(void)setHigherAndLower
{
float firstHeight = firstView.frame.size.height;
float secondHeight = secondView.frame.size.height;
float thridHeight = thridView.frame.size.height;
//比较哪一列是最高的那列,并记录最高的值highValue和最高的列higher
if (firstHeight > highValue) {
highValue = firstHeight;
higher = 1;
}else if (secondHeight > highValue){
highValue = secondHeight;
higher = 2;
}else if (thridHeight > highValue){
highValue = thridHeight;
higher = 3;
}
//找了最低列
if (firstHeight < secondHeight) {
if (firstHeight <= thridHeight) {
lower = 1;
}else{
lower = 3;
}
}else{
if (secondHeight <= thridHeight) {
lower = 2;
}else{
lower = 3;
}
}
}
下面就是创建添加图片视图的方法,同过上面不断刷新的最低列的值判断将创建的图片视图插入到那一列,其步骤就是
1、创建自定义的图片对象
2、记住该图片的高度
3、重新定义每列的大小,就是高度
4、把该图片加到每列上。
-(void)addViews:(ImageInfo *)image with:(int)a
{
//要添加到列上的图片对象
SelfImageVIew *imageView = nil;
//图片的高度
float imageHeight = 0;
//在最低的那一列添加图片
switch (lower) {
case 1:
imageView = [[SelfImageVIew alloc]initWithData:image yPoint:firstView.frame.size.height];
imageHeight = imageView.frame.size.height;
firstView.frame = CGRectMake(firstView.frame.origin.x, firstView.frame.origin.y, WIDTH, firstView.frame.size.height + imageHeight);
[firstView addSubview:imageView];
break;
case 2:
imageView = [[SelfImageVIew alloc]initWithData:image yPoint:secondView.frame.size.height];
imageHeight = imageView.frame.size.height;
secondView.frame = CGRectMake(secondView.frame.origin.x, secondView.frame.origin.y, WIDTH, secondView.frame.size.height + imageHeight);
[secondView addSubview:imageView];
break;
case 3:
imageView = [[SelfImageVIew alloc]initWithData:image yPoint:thridView.frame.size.height];
imageHeight = imageView.frame.size.height;
thridView.frame = CGRectMake(thridView.frame.origin.x, thridView.frame.origin.y, WIDTH, thridView.frame.size.height + imageHeight);
[thridView addSubview:imageView];
break;
default:
break;
}
}
以上代码实现了提取数据加载视图为瀑布流布局的基本功能,另想增强功能添加上拉加载的话,可以使用EGO框架控制器里调用以下方法
实现加载数据:
//刷新瀑布流
-(void)refreshView:(NSArray*)array
{
[firstView removeFromSuperview];
[secondView removeFromSuperview];
[thridView removeFromSuperview];
firstView = nil;
secondView = nil;
thridView = nil;
self.arrayImage = array;
[self initParameter];
}
//加载下一页瀑布流
-(void)loadNextPage:(NSArray*)array
{
for (int i = 0; i<array.count; i++) {
//0%3=0,0-2除3也不可能大于0
if (i/3>0 && i%3==0) {
row++;
}
ImageInfo *data = (ImageInfo*)[array objectAtIndex:i];
countImage++;
//添加视图
[self addViews:data with:countImage];
//重新设置最高和最低view
[self setHigherAndLower];
}
[self setContentSize:CGSizeMake(WIDTH, highValue)];
}
接下来优化瀑布流的性能,这就需要考虑到 UIScrollView的重用机制,我们经常使用UITableView的Cell重用,作为UITableView的父类在UIScrollView上我们可以仿照UItableView来实现相似的重用机制,
常见的重用原理都是: 如果scrollView向下或向下滚动,一旦一排视图滚出了可视范围,将出了可是范围的视图移除放进重用视图组中,在加载新的视图时将重用视图组中的视图添加上去。
---研究中未完---