瀑布流照片实现

本文介绍如何使用dispatch_once_t实现单例模式,并通过实际案例演示如何在iOS应用中实现瀑布流布局,包括图片加载、压缩及事件响应。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.dispatch_once_t
多线程技术,多用在类方法中用来返回一个单例;
检测每次调用时,block是否执行完毕。
实例:
1

+ (id)sharedRequest

{
    //利用gcd创建一个单例模式 用来上传图片

    static DVHttpConnectRequest *connectRequest = nil;

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        connectRequest = [[DVHttpConnectRequest alloc]init];

    });

    return connectRequest;`

2

+(ImageLoader *)shareInstance{
    static ImageLoader *loader = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,^{
        loader = [[ImageLoader alloc]init];
    });
    return loader;
}

我们看到,该方法的作用就是执行且在整个程序的声明周期中,仅执行一次某一个block对象。简直就是为单例而生的嘛。而且,有些我们需要在程序开头初始化的动作,如果为了保证其,仅执行一次,也可以放到这个dispatch_once来执行。

然后我们看到它需要一个断言来确定这个代码块是否执行,这个断言的指针要保存起来,相对于第一种方法而言,还需要多保存一个指针。

方法简介中就说的很清楚了:对于在应用中创建一个初始化一个全局的数据对象(单例模式),这个函数很有用。

如果同时在多线程中调用它,这个函数将等待同步等待,直至该block调用结束。

这个断言的指针必须要全局化的保存,或者放在静态区内。使用存放在自动分配区域或者动态区域的断言,dispatch_once执行的结果是不可预知的。

瀑布流实现:

#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的代码片段:
[objc] view plain copy 在CODE上查看代码片派生到我的代码片
#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来获取点击图片的名字,然后将名字传递给该类,完成对相应图片的显示,当然这里也要做对图片的压缩处理,使它显示的时候不失真。(由于本人有点懒,这个类的界面美观什么的就懒得调了,多多包涵)示意图如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值