想必大家已经对互联网传统的照片布局方式司空见惯了,这种行列分明的布局虽然对用户来说简洁明了,但是长久的使用难免会产生审美疲劳。现在网上流行一种叫做“瀑布流”的照片布局样式,这种行与列参差不齐的状态着实给用户眼前一亮的感觉,这种不规则的方式也吸引着我,现在我们就来一起实现它吧 :)
首先我们来看一下这种样式布局是如何体现的,请看示意图:

别看这种界面的布局好像毫无规律,其实它的排列还是很有规则的。我们拿手机屏幕举个例子,我们把屏幕等宽的划分为几个区域,由于手机屏幕比较小,我们就按照网上
最普遍的把屏幕分为等宽的三列,然后将图片加载在每一列中,在加入到列之前,要先判断哪一列的高度最低,然后把图片加到列高度最低的那列中。听起来是不是有些拗口,说简单点就是“哪列高度低就加哪”,这样听我一说是不是觉得原理其实很简单嘛!
下面我们就来用程序去实现他吧!首先看一张效果图。

新建一个Xcode工程,名字随便取吧(或者叫Album都可以),然后新建三个类,分别是:MainViewController用于控制主界面, MyScrollView继承自UIScrollView用于控制界面滚动,以及图片的管理类ImageLoader。
我们先来看一下ImageLoader类,它是一个图片的处理类,负责对取到的图片进行等比例压缩,以至于加载到UImageView中时不会失真,代码如下:

  1. #import "ImageLoader.h"
  2. @interface ImageLoader ()
  3. @end
  4. @implementation ImageLoader
  5. @synthesize imagesArray = _imagesArray;
  6. + (ImageLoader *)shareInstance{
  7. static ImageLoader *loader = nil;
  8. static dispatch_once_t onceToken;
  9. dispatch_once(&onceToken, ^{
  10. loader = [[ImageLoader alloc] init];
  11. });
  12. return loader;
  13. }
  14. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
  15. {
  16. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  17. if (self) {
  18. // Custom initialization
  19. }
  20. return self;
  21. }
  22. - (void)viewDidLoad
  23. {
  24. [super viewDidLoad];
  25. // Do any additional setup after loading the view.
  26. }
  27. /*加载图片*/
  28. - (void)loadImage:(NSMutableArray *)array{
  29. self.imagesArray = array;
  30. }
  31. /*
  32. 压缩图片,根据图片的大小按比例压缩
  33. width:每列试图的宽度
  34. 返回一个UIImageView
  35. */
  36. - (UIImageView *)compressImage:(float)width imageName:(NSString *)name{
  37. UIImageView *imgView = [[UIImageView alloc] init];
  38. imgView.image = [UIImage imageNamed:name];
  39. float orgi_width = [imgView image].size.width;
  40. float orgi_height = [imgView image].size.height;
  41. //按照每列的宽度,以及图片的宽高来按比例压缩
  42. float new_width = width - 5;
  43. float new_height = (width * orgi_height)/orgi_width;
  44. //重置imageView的尺寸
  45. [imgView setFrame:CGRectMake(0, 0, new_width, new_height)];
  46. return imgView;
  47. }
  48. - (void)didReceiveMemoryWarning
  49. {
  50. [super didReceiveMemoryWarning];
  51. // Dispose of any resources that can be recreated.
  52. }
  53. - (void)dealloc{
  54. [self.imagesArray release];
  55. [super dealloc];
  56. }
  57. @end

这里将ImageLoader类设置成了单例,以便于在MainViewContrioller和MyScrollView中获取。
下面是MyScrollView的代码片段:

  1. #import "MyScrollView.h"
  2. #define COORDINATE_X_LEFT 5
  3. #define COORDINATE_X_MIDDLE MY_WIDTH/3 + 5
  4. #define COORDINATE_X_RIGHT MY_WIDTH/3 * 2 + 5
  5. #define PAGESIZE 21
  6. @interface MyScrollView ()
  7. @end
  8. @implementation MyScrollView
  9. @synthesize mainScroll = _mainScroll;
  10. @synthesize isOnce = _isOnce;
  11. @synthesize imagesName = _imagesName;
  12. @synthesize loadedImageDic = _loadedImageDic;
  13. @synthesize leftColumHeight = _leftColumHeight;
  14. @synthesize midColumHeight = _midColumHeight;
  15. @synthesize rightColumHeight = _rightColumHeight;
  16. @synthesize loadedImageArray = _loadedImageArray;
  17. @synthesize imgTag = _imgTag;
  18. @synthesize imgTagDic = _imgTagDic;
  19. @synthesize imageLoad = _imageLoad;
  20. @synthesize page = _page;
  21. + (MyScrollView *)shareInstance{
  22. static MyScrollView *instance;
  23. static dispatch_once_t onceToken;
  24. dispatch_once(&onceToken, ^{
  25. instance = [[self alloc] initWithFrame:CGRectMake(0, 0, MY_WIDTH, MY_HEIGHT)];
  26. });
  27. return instance;
  28. }
  29. /*
  30. 初始化scrollView的委托以及背景颜色,不显示它的水平,垂直显示条
  31. */
  32. - (id)initWithFrame:(CGRect)frame{
  33. self = [super initWithFrame:frame];
  34. if(self){
  35. self.delegate = self;
  36. self.backgroundColor = [UIColor blackColor];
  37. self.pagingEnabled = NO;
  38. self.showsHorizontalScrollIndicator = NO;
  39. self.showsVerticalScrollIndicator = NO;
  40. self.isOnce = YES;
  41. self.loadedImageDic = [[NSMutableDictionary alloc] init];
  42. self.loadedImageArray = [[NSMutableArray alloc] init];
  43. self.imgTagDic = [[NSMutableDictionary alloc] init];
  44. //初始化列的高度
  45. self.leftColumHeight = 3.0f;
  46. self.midColumHeight = 3.0f;
  47. self.rightColumHeight = 3.0f;
  48. self.imgTag = 10086;
  49. self.page = 1;
  50. [self initWithPhotoBox];
  51. }
  52. return self;
  53. }
  54. /*
  55. 将scrollView界面分为大小相等的3个部分,每个部分为一个UIView, 并设置每一个UIView的tag
  56. */
  57. - (void)initWithPhotoBox{
  58. UIView *leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, MY_WIDTH/3, self.frame.size.height)];
  59. UIView *middleView = [[UIView alloc] initWithFrame:CGRectMake(leftView.frame.origin.x + MY_WIDTH/3, 0, MY_WIDTH/3,
  60. self.frame.size.height)];
  61. UIView *rightView = [[UIView alloc] initWithFrame:CGRectMake(middleView.frame.origin.x + MY_WIDTH/3, 0, MY_WIDTH/3,
  62. self.frame.size.height)];
  63. //设置三个部分的tag
  64. leftView.tag = 100;
  65. middleView.tag = 101;
  66. rightView.tag = 102;
  67. //设置背景颜色
  68. [leftView setBackgroundColor:[UIColor clearColor]];
  69. [middleView setBackgroundColor:[UIColor clearColor]];
  70. [rightView setBackgroundColor:[UIColor clearColor]];
  71. [self addSubview:leftView];
  72. [self addSubview:middleView];
  73. [self addSubview:rightView];
  74. //第一次加载图片
  75. self.imageLoad = [ImageLoader shareInstance];
  76. for(int i = 0; i < PAGESIZE; i++){
  77. NSString *imageName = [self.imageLoad.imagesArray objectAtIndex:i];
  78. UIImageView *imgView = [self.imageLoad compressImage:MY_WIDTH/3 imageName:imageName];
  79. [self addImage:imgView name:imageName];
  80. [self checkImageIsVisible];
  81. }
  82. //第一页
  83. self.page = 1;
  84. [self adjustContentSize:NO];
  85. }
  86. /*调整scrollview*/
  87. - (void)adjustContentSize:(BOOL)isEnd{
  88. UIView *leftView = [self viewWithTag:100];
  89. UIView *middleView = [self viewWithTag:101];
  90. UIView *rightView = [self viewWithTag:102];
  91. if(_leftColumHeight >= _midColumHeight && _leftColumHeight >= _rightColumHeight){
  92. self.contentSize = leftView.frame.size;
  93. }else{
  94. if(_midColumHeight >= _rightColumHeight){
  95. self.contentSize = middleView.frame.size;
  96. }else{
  97. self.contentSize = rightView.frame.size;
  98. }
  99. }
  100. }
  101. /*
  102. 得到最短列的高度
  103. */
  104. - (float)getTheShortColum{
  105. if(_leftColumHeight <= _midColumHeight && _leftColumHeight <= _rightColumHeight){
  106. return _leftColumHeight;
  107. }else{
  108. if(_midColumHeight <= _rightColumHeight){
  109. return _midColumHeight;
  110. }else{
  111. return _rightColumHeight;
  112. }
  113. }
  114. }
  115. /*
  116. 添加一张图片
  117. 规则:根据每一列的高度来决定,优先加载列高度最短的那列
  118. 重新设置图片的x,y坐标
  119. imageView:图片视图
  120. imageName:图片名
  121. */
  122. - (void)addImage:(UIImageView *)imageView name:(NSString *)imageName{
  123. //图片是否加载
  124. if([self.loadedImageDic objectForKey:imageName]){
  125. return;
  126. }
  127. //若图片还未加载则保存
  128. [self.loadedImageDic setObject:imageView forKey:imageName];
  129. [self.loadedImageArray addObject:imageView];
  130. [self imageTagWithAction:imageView name:imageName];
  131. float width = imageView.frame.size.width;
  132. float height = imageView.frame.size.height;
  133. //判断哪一列的高度最低
  134. if(_leftColumHeight <= _midColumHeight && _leftColumHeight <= _rightColumHeight){
  135. UIView *leftView = [self viewWithTag:100];
  136. [leftView addSubview:imageView];
  137. //重新设置坐标
  138. [imageView setFrame:CGRectMake(2, _leftColumHeight, width, height)];
  139. _leftColumHeight = _leftColumHeight + height + 3;
  140. [leftView setFrame:CGRectMake(0, 0, MY_WIDTH/3, _leftColumHeight)];
  141. }else{
  142. if(_midColumHeight <= _rightColumHeight){
  143. UIView *middleView = [self viewWithTag:101];
  144. [middleView addSubview:imageView];
  145. [imageView setFrame:CGRectMake(2, _midColumHeight, width, height)];
  146. _midColumHeight = _midColumHeight + height + 3;
  147. [middleView setFrame:CGRectMake(MY_WIDTH/3, 0, MY_WIDTH/3, _midColumHeight)];
  148. }else{
  149. UIView *rightView = [self viewWithTag:102];
  150. [rightView addSubview:imageView];
  151. [imageView setFrame:CGRectMake(2, _rightColumHeight, width, height)];
  152. _rightColumHeight = _rightColumHeight + height + 3;
  153. [rightView setFrame:CGRectMake(22 * MY_WIDTH/3, 0, MY_WIDTH/3, _rightColumHeight)];
  154. }
  155. }
  156. }
  157. /*
  158. 将图片tag保存,以及为UIImageView添加事件响应
  159. */
  160. - (void)imageTagWithAction:(UIImageView *)imageView name:(NSString *)imageName{
  161. //将要显示图片的tag保存
  162. imageView.tag = self.imgTag;
  163. [self.imgTagDic setObject:imageName forKey:[NSString stringWithFormat:@"%d", imageView.tag]];
  164. self.imgTag++;
  165. //图片添加事件响应
  166. UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageClickWithTag:)];
  167. tapRecognizer.delegate = self;
  168. imageView.userInteractionEnabled = YES;
  169. [imageView addGestureRecognizer:tapRecognizer];
  170. [tapRecognizer release];
  171. }
  172. /*
  173. //若三列中最短列距离底部高度超过30像素,则请求加载新的图片
  174. */
  175. - (void)scrollViewDidScroll:(UIScrollView *)scrollView{
  176. //可视检查
  177. [self checkImageIsVisible];
  178. if((self.contentOffset.y + self.frame.size.height) - [self getTheShortColum] > 30){
  179. [self pullRefreshImages];
  180. }
  181. }
  182. /*
  183. 上拉时加载新的图片
  184. */
  185. - (void)pullRefreshImages{
  186. int index = self.page *PAGESIZE;
  187. int imgNum = [self.imageLoad.imagesArray count];
  188. if(index >= imgNum){
  189. //图片加载完毕
  190. [self adjustContentSize:YES];
  191. [MyToast showWithText:@"没有更多图片"];
  192. }else{
  193. if((imgNum - self.page*PAGESIZE) > PAGESIZE){
  194. for (int i = index; i < PAGESIZE; i++) {
  195. NSString *imageName = [self.imageLoad.imagesArray objectAtIndex:i];
  196. UIImageView *imgView = [self.imageLoad compressImage:MY_WIDTH/3 imageName:imageName];
  197. [self addImage:imgView name:imageName];
  198. [self checkImageIsVisible];
  199. }
  200. }else{
  201. for (int i = index; i < imgNum; i++) {
  202. NSString *imageName = [self.imageLoad.imagesArray objectAtIndex:i];
  203. UIImageView *imgView = [self.imageLoad compressImage:MY_WIDTH/3 imageName:imageName];
  204. [self addImage:imgView name:imageName];
  205. [self checkImageIsVisible];
  206. }
  207. }
  208. self.page++;
  209. }
  210. [self adjustContentSize:NO];
  211. }
  212. /*
  213. 检查图片是否可见,如果不在可见视线内,则把图片替换为nil
  214. */
  215. - (void)checkImageIsVisible{
  216. for (int i = 0; i < [self.loadedImageArray count]; i++) {
  217. UIImageView *imgView = [self.loadedImageArray objectAtIndex:i];
  218. if((self.contentOffset.y - imgView.frame.origin.y) > imgView.frame.size.height ||
  219. imgView.frame.origin.y > (self.frame.size.height + self.contentOffset.y)){
  220. //不显示图片
  221. imgView.image = nil;
  222. }else{
  223. //重新根据tag值显示图片
  224. NSString *imageName = [self.imgTagDic objectForKey:[NSString stringWithFormat:@"%d", imgView.tag]];
  225. if((NSNull *)imageName == [NSNull null]){
  226. return;
  227. }
  228. UIImageView *view = [self.imageLoad compressImage:MY_WIDTH/3 imageName:imageName];
  229. imgView.image = view.image;
  230. }
  231. }
  232. }
  233. //点击图片事件响应
  234. - (void)imageClickWithTag:(UITapGestureRecognizer *)sender{
  235. UIImageView *view = (UIImageView *)sender.view;
  236. NSString *imageName = [self.imgTagDic objectForKey:[NSString stringWithFormat:@"%d", view.tag]];
  237. NSLog(@"%@", imageName);
  238. PhotoViewController *photoView = [[PhotoViewController alloc] init];
  239. photoView.imageName = imageName;
  240. [self addSubview:photoView.view];
  241. }
  242. - (void)dealloc{
  243. [self.imagesName release];
  244. [self.imgTagDic release];
  245. [self.loadedImageArray release];
  246. [super dealloc];
  247. }
  248. @end

在这个类中,我将三列等宽的UIView加入到ScrollView中,每次加入一张图片的时候都会去判断一下哪一列的高度最低。为了避免手机被图片的占用内存过高导致程序
崩溃的问题,我还作了一个照片可见性的逻辑判断,具体函数名为checkImageIsVisible,当照片不可见时,将imageView中的image设为nil,一旦照片出现了就根据照片的tag
获取到照片名重新设置image。
最后,当照片加载出来以后当然不能少了点击查看相册的功能啊!于是新建一个UIViewController类取名为:PhotoViewController,代码如下:

  1. #import "PhotoViewController.h"
  2. @interface PhotoViewController ()
  3. @end
  4. @implementation PhotoViewController
  5. @synthesize headView = _headView;
  6. @synthesize mainView = _mainView;
  7. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
  8. {
  9. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  10. if (self) {
  11. // Custom initialization
  12. }
  13. return self;
  14. }
  15. - (void)viewDidLoad
  16. {
  17. [super viewDidLoad];
  18. // Do any additional setup after loading the view.
  19. [self.view setFrame:CGRectMake(0, 0, MY_WIDTH, MY_HEIGHT)];
  20. [self.view setBackgroundColor:[UIColor blackColor]];
  21. self.headView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, MY_WIDTH, 50)];
  22. UIButton *cancelBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 20, 40, 30)];
  23. [cancelBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  24. [cancelBtn setTitle:@"取消" forState:UIControlStateNormal];
  25. [self.headView addSubview:cancelBtn];
  26. [cancelBtn addTarget:self action:@selector(closePhotoView) forControlEvents:UIControlEventTouchUpInside];
  27. [self.view addSubview:self.headView];
  28. self.mainView = [[UIView alloc] initWithFrame:CGRectMake(0, 80, MY_WIDTH, MY_HEIGHT)];
  29. [self.view addSubview:self.mainView];
  30. }
  31. - (void)viewWillAppear:(BOOL)animated{
  32. UIImageView *imageV = [self compressImage:MY_WIDTH imageName:_imageName];
  33. [self.mainView addSubview:imageV];
  34. }
  35. - (UIImageView *)compressImage:(float)width imageName:(NSString *)name{
  36. UIImageView *imgView = [[UIImageView alloc] init];
  37. imgView.image = [UIImage imageNamed:name];
  38. float orgi_width = [imgView image].size.width;
  39. float orgi_height = [imgView image].size.height;
  40. //按照每列的宽度,以及图片的宽高来按比例压缩
  41. float new_width = width - 5;
  42. float new_height = (width * orgi_height)/orgi_width;
  43. //重置imageView的尺寸
  44. [imgView setFrame:CGRectMake(0, 0, new_width, new_height)];
  45. return imgView;
  46. }
  47. - (void)didReceiveMemoryWarning
  48. {
  49. [super didReceiveMemoryWarning];
  50. // Dispose of any resources that can be recreated.
  51. }
  52. //关闭
  53. - (void)closePhotoView{
  54. [self.view removeFromSuperview];
  55. }
  56. - (void)dealloc{
  57. [self.headView release];
  58. [self.mainView release];
  59. [super dealloc];
  60. }
  61. @end

这个类主要是用于显示大图片所用,用户点击小图片后,程序根据图片的tag来获取点击图片的名字,然后将名字传递给该类,完成对相应图片的显示,当然这里也要做对图片的压缩处理,使它显示的时候不失真。(由于本人有点懒,这个类的界面美观什么的就懒得调了,多多包涵)示意图如下:

以上就是这个例子的大部分代码了,如果要下载完整工程的话,请到我的上传中下载。

现在看一下整个效果;

IOS开发之瀑布流照片墙实现的更多相关文章

  1. Android瀑布流照片墙实现,体验不规则排列的美感

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/10470797 传统界面的布局方式总是行列分明.坐落有序的,这种布局已是司空见惯,在 ...

  2. [JS练习] 瀑布流照片墙

    记录JS实现瀑布流照片墙效果 首先是前端页面 <!DOCTYPE html> <html lang="en"> <head> <meta ...

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

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

  4. Android不规则瀑布流照片墙的实现+LruCache算法

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZnJhbmNpc3NoaQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...

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

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

  6. android瀑布流照片墙实现代码详解

    照片墙的实现,是需要往手机里面添加很多图片的,如果没有对资源进行合理的释放,程序很快就会出现OOM.所以需要用到LruCache算法来缓存图片. 1,首先是图片资源类,这个类中包含了很多图片链接. p ...

  7. iOS UITabView简写瀑布流

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

  8. Mvc利用淘宝Kissy uploader实现图片批量上传附带瀑布流的照片墙

    前言 KISSY 是由阿里集团前端工程师们发起创建的一个开源 JS 框架.它具备模块化.高扩展性.组件齐全,接口一致.自主开发.适合多种应用场景等特性.本人在一次项目中层使用这个uploader组件. ...

  9. Mvc Kissy uploader实现图片批量上传 附带瀑布流的照片墙

    前言 KISSY 是由阿里集团前端工程师们发起创建的一个开源 JS 框架.它具备模块化.高扩展性.组件齐全,接口一致.自主开发.适合多种应用场景等特性.本人在一次项目中层使用这个uploader组件. ...

随机推荐

  1. 【ContestHunter】【弱省胡策】【Round4】

    01分数规划(网络流)+状压DP+树形DP 官方题解地址:http://pan.baidu.com/s/1mg5S5z6 A 好神啊= =第一次写01分数规划 其实分数规划是要求$$ Maximize ...

  2. Linux系统教程 标准输入/输出和重定向

    1. 标准输入与输出 我们知道,执行一个shell命令行时通常会自动打开三个标准文件,即标准输入文件(stdin),通常对应终端的键盘:标准输出文件(stdout)和标准错误输出文件(stderr), ...

  3. HTML5, CSS3, ES5新的web标准和浏览器支持一览 转

    本文整理了一些最重要(或者说人气比较高罢)的新标准,虽然它们多数还只是w3c的草案,离Recommendation级别还早,却已经成为新一轮浏览器大战中备受追捧的明星,开发者社区里也涌现出大量相关的d ...

  4. 浅谈jQuery easyui datagrid操作单元格样式

    今天项目上遇到问题,就是表格风格统一的问题,由于用了2个不同的框架,所以如果要大修比较麻烦,考虑到修改表格样式工作量会少很多,所以考虑修改jQuery easyui datagrid数据网格的样式. ...

  5. php CURL 请求头和响应头获取

    1.从CURL中获取响应头 $oCurl = curl_init(); // 设置请求头, 有时候需要,有时候不用,看请求网址是否有对应的要求 $header[] = "Content-ty ...

  6. Cognos中新建SQLserver数据源的步骤

    1:配置-数据源连接-新建数据源-指定数据源名称 2:选择数据库类型,暂时不配置jdbc 3:指定服务器,数据库名称,登陆用户名和密码 4:测试 5:测试OK(OLE DB类型的) 6:返回上一步 , ...

  7. (转)ngui3.5.7 版本Scroll View实现方法

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://xyo123.blog.51cto.com/6369437/1405861 现在网 ...

  8. Your Customers Do Not Mean What They Say

    Your Customers Do Not Mean What They Say Nate Jackson I'VE NEVER MET A CUSTOMER YET that wasn't all ...

  9. 【转】允许远程用户登录访问mysql的方法

    需要手动增加可以远程访问数据库的用户. 方法一.本地登入mysql,更改 "mysql" 数据库里的 "user" 表里的 "host" 项 ...

  10. ASP.NET Page执行顺序如:OnPreInit()、OnInit()

    http://www.cnblogs.com/yeminglong/archive/2012/10/16/2725664.html 当页面进行回发时,如点击按钮,以上事件都会重新执行一次,这时的执行顺 ...