IOS开发之瀑布流照片墙实现
想必大家已经对互联网传统的照片布局方式司空见惯了,这种行列分明的布局虽然对用户来说简洁明了,但是长久的使用难免会产生审美疲劳。现在网上流行一种叫做“瀑布流”的照片布局样式,这种行与列参差不齐的状态着实给用户眼前一亮的感觉,这种不规则的方式也吸引着我,现在我们就来一起实现它吧 :)
首先我们来看一下这种样式布局是如何体现的,请看示意图:
别看这种界面的布局好像毫无规律,其实它的排列还是很有规则的。我们拿手机屏幕举个例子,我们把屏幕等宽的划分为几个区域,由于手机屏幕比较小,我们就按照网上
最普遍的把屏幕分为等宽的三列,然后将图片加载在每一列中,在加入到列之前,要先判断哪一列的高度最低,然后把图片加到列高度最低的那列中。听起来是不是有些拗口,说简单点就是“哪列高度低就加哪”,这样听我一说是不是觉得原理其实很简单嘛!
下面我们就来用程序去实现他吧!首先看一张效果图。
新建一个Xcode工程,名字随便取吧(或者叫Album都可以),然后新建三个类,分别是:MainViewController用于控制主界面, MyScrollView继承自UIScrollView用于控制界面滚动,以及图片的管理类ImageLoader。
我们先来看一下ImageLoader类,它是一个图片的处理类,负责对取到的图片进行等比例压缩,以至于加载到UImageView中时不会失真,代码如下:
- #import "ImageLoader.h"
- @interface ImageLoader ()
- @end
- @implementation ImageLoader
- @synthesize imagesArray = _imagesArray;
- + (ImageLoader *)shareInstance{
- static ImageLoader *loader = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- loader = [[ImageLoader alloc] init];
- });
- return loader;
- }
- - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
- {
- self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
- if (self) {
- // Custom initialization
- }
- return self;
- }
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- // Do any additional setup after loading the view.
- }
- /*加载图片*/
- - (void)loadImage:(NSMutableArray *)array{
- self.imagesArray = array;
- }
- /*
- 压缩图片,根据图片的大小按比例压缩
- width:每列试图的宽度
- 返回一个UIImageView
- */
- - (UIImageView *)compressImage:(float)width imageName:(NSString *)name{
- UIImageView *imgView = [[UIImageView alloc] init];
- imgView.image = [UIImage imageNamed:name];
- float orgi_width = [imgView image].size.width;
- float orgi_height = [imgView image].size.height;
- //按照每列的宽度,以及图片的宽高来按比例压缩
- float new_width = width - 5;
- float new_height = (width * orgi_height)/orgi_width;
- //重置imageView的尺寸
- [imgView setFrame:CGRectMake(0, 0, new_width, new_height)];
- return imgView;
- }
- - (void)didReceiveMemoryWarning
- {
- [super didReceiveMemoryWarning];
- // Dispose of any resources that can be recreated.
- }
- - (void)dealloc{
- [self.imagesArray release];
- [super dealloc];
- }
- @end
这里将ImageLoader类设置成了单例,以便于在MainViewContrioller和MyScrollView中获取。
下面是MyScrollView的代码片段:
- #import "MyScrollView.h"
- #define COORDINATE_X_LEFT 5
- #define COORDINATE_X_MIDDLE MY_WIDTH/3 + 5
- #define COORDINATE_X_RIGHT MY_WIDTH/3 * 2 + 5
- #define PAGESIZE 21
- @interface MyScrollView ()
- @end
- @implementation MyScrollView
- @synthesize mainScroll = _mainScroll;
- @synthesize isOnce = _isOnce;
- @synthesize imagesName = _imagesName;
- @synthesize loadedImageDic = _loadedImageDic;
- @synthesize leftColumHeight = _leftColumHeight;
- @synthesize midColumHeight = _midColumHeight;
- @synthesize rightColumHeight = _rightColumHeight;
- @synthesize loadedImageArray = _loadedImageArray;
- @synthesize imgTag = _imgTag;
- @synthesize imgTagDic = _imgTagDic;
- @synthesize imageLoad = _imageLoad;
- @synthesize page = _page;
- + (MyScrollView *)shareInstance{
- static MyScrollView *instance;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- instance = [[self alloc] initWithFrame:CGRectMake(0, 0, MY_WIDTH, MY_HEIGHT)];
- });
- return instance;
- }
- /*
- 初始化scrollView的委托以及背景颜色,不显示它的水平,垂直显示条
- */
- - (id)initWithFrame:(CGRect)frame{
- self = [super initWithFrame:frame];
- if(self){
- self.delegate = self;
- self.backgroundColor = [UIColor blackColor];
- self.pagingEnabled = NO;
- self.showsHorizontalScrollIndicator = NO;
- self.showsVerticalScrollIndicator = NO;
- self.isOnce = YES;
- self.loadedImageDic = [[NSMutableDictionary alloc] init];
- self.loadedImageArray = [[NSMutableArray alloc] init];
- self.imgTagDic = [[NSMutableDictionary alloc] init];
- //初始化列的高度
- self.leftColumHeight = 3.0f;
- self.midColumHeight = 3.0f;
- self.rightColumHeight = 3.0f;
- self.imgTag = 10086;
- self.page = 1;
- [self initWithPhotoBox];
- }
- return self;
- }
- /*
- 将scrollView界面分为大小相等的3个部分,每个部分为一个UIView, 并设置每一个UIView的tag
- */
- - (void)initWithPhotoBox{
- UIView *leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, MY_WIDTH/3, self.frame.size.height)];
- UIView *middleView = [[UIView alloc] initWithFrame:CGRectMake(leftView.frame.origin.x + MY_WIDTH/3, 0, MY_WIDTH/3,
- self.frame.size.height)];
- UIView *rightView = [[UIView alloc] initWithFrame:CGRectMake(middleView.frame.origin.x + MY_WIDTH/3, 0, MY_WIDTH/3,
- self.frame.size.height)];
- //设置三个部分的tag
- leftView.tag = 100;
- middleView.tag = 101;
- rightView.tag = 102;
- //设置背景颜色
- [leftView setBackgroundColor:[UIColor clearColor]];
- [middleView setBackgroundColor:[UIColor clearColor]];
- [rightView setBackgroundColor:[UIColor clearColor]];
- [self addSubview:leftView];
- [self addSubview:middleView];
- [self addSubview:rightView];
- //第一次加载图片
- self.imageLoad = [ImageLoader shareInstance];
- for(int i = 0; i < PAGESIZE; i++){
- NSString *imageName = [self.imageLoad.imagesArray objectAtIndex:i];
- UIImageView *imgView = [self.imageLoad compressImage:MY_WIDTH/3 imageName:imageName];
- [self addImage:imgView name:imageName];
- [self checkImageIsVisible];
- }
- //第一页
- self.page = 1;
- [self adjustContentSize:NO];
- }
- /*调整scrollview*/
- - (void)adjustContentSize:(BOOL)isEnd{
- UIView *leftView = [self viewWithTag:100];
- UIView *middleView = [self viewWithTag:101];
- UIView *rightView = [self viewWithTag:102];
- if(_leftColumHeight >= _midColumHeight && _leftColumHeight >= _rightColumHeight){
- self.contentSize = leftView.frame.size;
- }else{
- if(_midColumHeight >= _rightColumHeight){
- self.contentSize = middleView.frame.size;
- }else{
- self.contentSize = rightView.frame.size;
- }
- }
- }
- /*
- 得到最短列的高度
- */
- - (float)getTheShortColum{
- if(_leftColumHeight <= _midColumHeight && _leftColumHeight <= _rightColumHeight){
- return _leftColumHeight;
- }else{
- if(_midColumHeight <= _rightColumHeight){
- return _midColumHeight;
- }else{
- return _rightColumHeight;
- }
- }
- }
- /*
- 添加一张图片
- 规则:根据每一列的高度来决定,优先加载列高度最短的那列
- 重新设置图片的x,y坐标
- imageView:图片视图
- imageName:图片名
- */
- - (void)addImage:(UIImageView *)imageView name:(NSString *)imageName{
- //图片是否加载
- if([self.loadedImageDic objectForKey:imageName]){
- return;
- }
- //若图片还未加载则保存
- [self.loadedImageDic setObject:imageView forKey:imageName];
- [self.loadedImageArray addObject:imageView];
- [self imageTagWithAction:imageView name:imageName];
- float width = imageView.frame.size.width;
- float height = imageView.frame.size.height;
- //判断哪一列的高度最低
- if(_leftColumHeight <= _midColumHeight && _leftColumHeight <= _rightColumHeight){
- UIView *leftView = [self viewWithTag:100];
- [leftView addSubview:imageView];
- //重新设置坐标
- [imageView setFrame:CGRectMake(2, _leftColumHeight, width, height)];
- _leftColumHeight = _leftColumHeight + height + 3;
- [leftView setFrame:CGRectMake(0, 0, MY_WIDTH/3, _leftColumHeight)];
- }else{
- if(_midColumHeight <= _rightColumHeight){
- UIView *middleView = [self viewWithTag:101];
- [middleView addSubview:imageView];
- [imageView setFrame:CGRectMake(2, _midColumHeight, width, height)];
- _midColumHeight = _midColumHeight + height + 3;
- [middleView setFrame:CGRectMake(MY_WIDTH/3, 0, MY_WIDTH/3, _midColumHeight)];
- }else{
- UIView *rightView = [self viewWithTag:102];
- [rightView addSubview:imageView];
- [imageView setFrame:CGRectMake(2, _rightColumHeight, width, height)];
- _rightColumHeight = _rightColumHeight + height + 3;
- [rightView setFrame:CGRectMake(22 * MY_WIDTH/3, 0, MY_WIDTH/3, _rightColumHeight)];
- }
- }
- }
- /*
- 将图片tag保存,以及为UIImageView添加事件响应
- */
- - (void)imageTagWithAction:(UIImageView *)imageView name:(NSString *)imageName{
- //将要显示图片的tag保存
- imageView.tag = self.imgTag;
- [self.imgTagDic setObject:imageName forKey:[NSString stringWithFormat:@"%d", imageView.tag]];
- self.imgTag++;
- //图片添加事件响应
- UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageClickWithTag:)];
- tapRecognizer.delegate = self;
- imageView.userInteractionEnabled = YES;
- [imageView addGestureRecognizer:tapRecognizer];
- [tapRecognizer release];
- }
- /*
- //若三列中最短列距离底部高度超过30像素,则请求加载新的图片
- */
- - (void)scrollViewDidScroll:(UIScrollView *)scrollView{
- //可视检查
- [self checkImageIsVisible];
- if((self.contentOffset.y + self.frame.size.height) - [self getTheShortColum] > 30){
- [self pullRefreshImages];
- }
- }
- /*
- 上拉时加载新的图片
- */
- - (void)pullRefreshImages{
- int index = self.page *PAGESIZE;
- int imgNum = [self.imageLoad.imagesArray count];
- if(index >= imgNum){
- //图片加载完毕
- [self adjustContentSize:YES];
- [MyToast showWithText:@"没有更多图片"];
- }else{
- if((imgNum - self.page*PAGESIZE) > PAGESIZE){
- for (int i = index; i < PAGESIZE; i++) {
- NSString *imageName = [self.imageLoad.imagesArray objectAtIndex:i];
- UIImageView *imgView = [self.imageLoad compressImage:MY_WIDTH/3 imageName:imageName];
- [self addImage:imgView name:imageName];
- [self checkImageIsVisible];
- }
- }else{
- for (int i = index; i < imgNum; i++) {
- NSString *imageName = [self.imageLoad.imagesArray objectAtIndex:i];
- UIImageView *imgView = [self.imageLoad compressImage:MY_WIDTH/3 imageName:imageName];
- [self addImage:imgView name:imageName];
- [self checkImageIsVisible];
- }
- }
- self.page++;
- }
- [self adjustContentSize:NO];
- }
- /*
- 检查图片是否可见,如果不在可见视线内,则把图片替换为nil
- */
- - (void)checkImageIsVisible{
- for (int i = 0; i < [self.loadedImageArray count]; i++) {
- UIImageView *imgView = [self.loadedImageArray objectAtIndex:i];
- if((self.contentOffset.y - imgView.frame.origin.y) > imgView.frame.size.height ||
- imgView.frame.origin.y > (self.frame.size.height + self.contentOffset.y)){
- //不显示图片
- imgView.image = nil;
- }else{
- //重新根据tag值显示图片
- NSString *imageName = [self.imgTagDic objectForKey:[NSString stringWithFormat:@"%d", imgView.tag]];
- if((NSNull *)imageName == [NSNull null]){
- return;
- }
- UIImageView *view = [self.imageLoad compressImage:MY_WIDTH/3 imageName:imageName];
- imgView.image = view.image;
- }
- }
- }
- //点击图片事件响应
- - (void)imageClickWithTag:(UITapGestureRecognizer *)sender{
- UIImageView *view = (UIImageView *)sender.view;
- NSString *imageName = [self.imgTagDic objectForKey:[NSString stringWithFormat:@"%d", view.tag]];
- NSLog(@"%@", imageName);
- PhotoViewController *photoView = [[PhotoViewController alloc] init];
- photoView.imageName = imageName;
- [self addSubview:photoView.view];
- }
- - (void)dealloc{
- [self.imagesName release];
- [self.imgTagDic release];
- [self.loadedImageArray release];
- [super dealloc];
- }
- @end
在这个类中,我将三列等宽的UIView加入到ScrollView中,每次加入一张图片的时候都会去判断一下哪一列的高度最低。为了避免手机被图片的占用内存过高导致程序
崩溃的问题,我还作了一个照片可见性的逻辑判断,具体函数名为checkImageIsVisible,当照片不可见时,将imageView中的image设为nil,一旦照片出现了就根据照片的tag
获取到照片名重新设置image。
最后,当照片加载出来以后当然不能少了点击查看相册的功能啊!于是新建一个UIViewController类取名为:PhotoViewController,代码如下:
- #import "PhotoViewController.h"
- @interface PhotoViewController ()
- @end
- @implementation PhotoViewController
- @synthesize headView = _headView;
- @synthesize mainView = _mainView;
- - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
- {
- self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
- if (self) {
- // Custom initialization
- }
- return self;
- }
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- // Do any additional setup after loading the view.
- [self.view setFrame:CGRectMake(0, 0, MY_WIDTH, MY_HEIGHT)];
- [self.view setBackgroundColor:[UIColor blackColor]];
- self.headView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, MY_WIDTH, 50)];
- UIButton *cancelBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 20, 40, 30)];
- [cancelBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
- [cancelBtn setTitle:@"取消" forState:UIControlStateNormal];
- [self.headView addSubview:cancelBtn];
- [cancelBtn addTarget:self action:@selector(closePhotoView) forControlEvents:UIControlEventTouchUpInside];
- [self.view addSubview:self.headView];
- self.mainView = [[UIView alloc] initWithFrame:CGRectMake(0, 80, MY_WIDTH, MY_HEIGHT)];
- [self.view addSubview:self.mainView];
- }
- - (void)viewWillAppear:(BOOL)animated{
- UIImageView *imageV = [self compressImage:MY_WIDTH imageName:_imageName];
- [self.mainView addSubview:imageV];
- }
- - (UIImageView *)compressImage:(float)width imageName:(NSString *)name{
- UIImageView *imgView = [[UIImageView alloc] init];
- imgView.image = [UIImage imageNamed:name];
- float orgi_width = [imgView image].size.width;
- float orgi_height = [imgView image].size.height;
- //按照每列的宽度,以及图片的宽高来按比例压缩
- float new_width = width - 5;
- float new_height = (width * orgi_height)/orgi_width;
- //重置imageView的尺寸
- [imgView setFrame:CGRectMake(0, 0, new_width, new_height)];
- return imgView;
- }
- - (void)didReceiveMemoryWarning
- {
- [super didReceiveMemoryWarning];
- // Dispose of any resources that can be recreated.
- }
- //关闭
- - (void)closePhotoView{
- [self.view removeFromSuperview];
- }
- - (void)dealloc{
- [self.headView release];
- [self.mainView release];
- [super dealloc];
- }
- @end
这个类主要是用于显示大图片所用,用户点击小图片后,程序根据图片的tag来获取点击图片的名字,然后将名字传递给该类,完成对相应图片的显示,当然这里也要做对图片的压缩处理,使它显示的时候不失真。(由于本人有点懒,这个类的界面美观什么的就懒得调了,多多包涵)示意图如下:
以上就是这个例子的大部分代码了,如果要下载完整工程的话,请到我的上传中下载。
现在看一下整个效果;
IOS开发之瀑布流照片墙实现的更多相关文章
- Android瀑布流照片墙实现,体验不规则排列的美感
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/10470797 传统界面的布局方式总是行列分明.坐落有序的,这种布局已是司空见惯,在 ...
- [JS练习] 瀑布流照片墙
记录JS实现瀑布流照片墙效果 首先是前端页面 <!DOCTYPE html> <html lang="en"> <head> <meta ...
- iOS之简单瀑布流的实现
iOS之简单瀑布流的实现 前言 超简单的瀑布流实现,这里说一下笔者的思路,详细代码在这里. 实现思路 collectionView能实现各中吊炸天的布局,其精髓就在于UICollectionVie ...
- Android不规则瀑布流照片墙的实现+LruCache算法
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZnJhbmNpc3NoaQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...
- iOS开发 - AVPlayer实现流音频边播边存
边播边下有三套左右实现思路,本文使用AVPlayer + AVURLAsset实现. 概述 1. AVPlayer简介 AVPlayer存在于AVFoundation中,可以播放视频和音频,可以理解为 ...
- android瀑布流照片墙实现代码详解
照片墙的实现,是需要往手机里面添加很多图片的,如果没有对资源进行合理的释放,程序很快就会出现OOM.所以需要用到LruCache算法来缓存图片. 1,首先是图片资源类,这个类中包含了很多图片链接. p ...
- iOS UITabView简写瀑布流
代码demo 一.tabViewCell,通过image的比例高算出cell 的高度 #import "TableViewCell.h" @implementation Table ...
- Mvc利用淘宝Kissy uploader实现图片批量上传附带瀑布流的照片墙
前言 KISSY 是由阿里集团前端工程师们发起创建的一个开源 JS 框架.它具备模块化.高扩展性.组件齐全,接口一致.自主开发.适合多种应用场景等特性.本人在一次项目中层使用这个uploader组件. ...
- Mvc Kissy uploader实现图片批量上传 附带瀑布流的照片墙
前言 KISSY 是由阿里集团前端工程师们发起创建的一个开源 JS 框架.它具备模块化.高扩展性.组件齐全,接口一致.自主开发.适合多种应用场景等特性.本人在一次项目中层使用这个uploader组件. ...
随机推荐
- C语言编译器不检查数组下标越界
这两天被人问了一个问题说假如C/C++访问下表越界的数组元素会报错么,于是充满好奇心的我动手试了一下,WTF,果然没有报错,但是会给程序带来莫名其妙的结果(比如十次的循环但是变成了死循环,但八次却可以 ...
- BFS(广搜)DFS(深搜)算法解析
图是一种灵活的数据结构,一般作为一种模型用来定义对象之间的关系或联系.对象由顶点(V)表示,而对象之间的关系或者关联则通过图的边(E)来表示. 图可以分为有向图和无向图,一般用G=(V,E)来表示图. ...
- linux下添加分区并挂载目录、卸载并删除分区
添加分区并挂载目录 Linux的硬盘识别: 一般使用”fdisk -l”命令可以列出系统中当前连接的硬盘 设备和分区信息.新硬盘没有分区信息,则只显示硬盘大小信息. 1.关闭服务器加上新硬盘 ...
- OTL翻译(10) -- OTL的流缓冲池
OTL的流缓冲池 一般来讲,流一般作为一个局部的变量被使用,当使用完毕后就立刻关闭,如果需要再次使用就需要再次的声明变量,如此循环.OTL流的缓冲池(内存池)是一个解决以往的流性能低下的一个机制.当流 ...
- mahout源码分析之DistributedLanczosSolver(五)Job over
Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. 1. Job 篇 接上篇,分析到EigenVerificationJob的run方法: public i ...
- CKEditor && CKFinder 配置
准备 ...
- angular6 Can't bind to 'zzst' since it isn't a known property of
文档: https://angular.io/guide/template-syntax#event-binding The Angular compiler may reject these bin ...
- php5.2以下版本无json_decode函数的解决办法
function json_decode2($json) { $comment = false; $out = '$x='; for ($i=0; $i<strlen($json); $i+ ...
- Jmeter-Maven-Plugin高级应用:Modifying Properties
Modifying Properties Pages 12 Home Adding additional libraries to the classpath Advanced Configurati ...
- sqoop安装部署(笔记)
sqoop是一个把关系型数据库数据抽向hadoop的工具.同时,也支持将hive.pig等查询的结果导入关系型数据库中存储.由于,笔者部署的hadoop版本是2.2.0,所以sqoop的版本是:sqo ...