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