zoukankan      html  css  js  c++  java
  • IOS开发之瀑布流照片墙实现

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

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

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

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

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

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

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

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

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

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

    现在看一下整个效果;

  • 相关阅读:
    高内聚 低耦合
    关系型数据库-三范式
    Excel 批量重命名照片
    完整性约束
    testCompile failed: multiple points
    Java日志体系
    Mac 修改HostName
    mac 配置/etc/profile重启后不生效
    mac命令行配置
    logback删除日志
  • 原文地址:https://www.cnblogs.com/fengmin/p/4973676.html
Copyright © 2011-2022 走看看