在iOS开发过程中经常需要通过网络请求加载图片,有时,需要在创建UIImageView或UIButton来显示图片之前需要提前知道图片的尺寸,根据图片尺寸创建对应大小的控件。但是对于网络图片来说,要想通过最优的方法获得尺寸就略微有点困难,大体思路有这么几种:
1.通过服务器处理。即在下行图片路径时拼接该图片的宽高。这种方法最简单,避免了不必要的网络请求,只需要从URL中截取即可(需要前后台的配合);
2.网络请求。如果有使用SDWebImage,则首先检查是否缓存过该图片,如果没有,先通过文件头获取图片大小(针对格式为png、gif、jpg文件获取其尺寸大小),如果获取失败,则下载完整的图片data,然后计算大小,如果有使用SDWebImage,则使用SDWebImage缓存该图片。
(方法二的最大的缺点就是虽然获取头文件耗时比下载图片快很多,但是还是有延迟,会阻塞主线程UI的绘制,如果网速不好的话会影响用户体验;但是如果使用了SDWebImage缓存,只会在初次加载图片的时候卡顿而已。)
代码如下:
//一个项目里面可能有好几个类都需要实现单例模式。为了更高效的编码,可以利用c语言中宏定义来实现。
//首先宏定义一个单例实现(在.pch/Header文件中拷贝如下代码加入即可)
//这里假设了实例的分享方法叫 shared"className"
//因为方法名 shared"className"是连在一起的,为了让宏能够正确替换掉签名中的“className”需要在前面加上 ##
//当宏的定义超过一行时,在末尾加上“\”表示下一行也在宏定义范围内。
//注意最后一行不需要加"\”。
// @interface#define singleton_interface(className) \+ (className *)shared##className;// @implementation#define singleton_implementation(className) \static className *_instance; \+ (id)allocWithZone:(NSZone *)zone \{ \static dispatch_once_t onceToken; \dispatch_once(&onceToken, ^{ \_instance = [super allocWithZone:zone]; \}); \return _instance; \} \+ (className *)shared##className \{ \static dispatch_once_t onceToken; \dispatch_once(&onceToken, ^{ \_instance = [[self alloc] init]; \}); \return _instance; \}********************************************************************然后头文件如下#import 《Foundation/Foundation.h》//特殊字符限制,将书名号改为尖括号#import "Singleton.h"@interface BLImageSize : NSObjectsingleton_interface(BLImageSize) //公共的访问单例对象的方法+(CGSize)downloadImageSizeWithURL:(id)imageURL;@end*************************************************************************最后是.m文件实现#import "BLImageSize.h"#import "SDImageCache.h"//如果未使用SDWebImage,则忽略@implementation BLImageSizesingleton_implementation(BLImageSize)//传入的参数imageURL可以为NSString类型,也可以为NSURL类型+(CGSize)getImageSizeWithURL:(id)imageURL{ NSURL* URL = nil; if([imageURL isKindOfClass:[NSURL class]]){ URL = imageURL; } if([imageURL isKindOfClass:[NSString class]]){ URL = [NSURL URLWithString:imageURL]; } if(URL == nil) { return CGSizeZero; // url为空则返回CGSizeZero} NSString* absoluteString = URL.absoluteString; //如果未使用SDWebImage,则忽略;检查是否缓存过该图片#ifdef dispatch_main_sync_safe if([[SDImageCache sharedImageCache] diskImageExistsWithKey:absoluteString]) { UIImage* image = [[SDImageCache sharedImageCache] imageFromMemoryCacheForKey:absoluteString]; if(!image) { NSData* data = [[SDImageCache sharedImageCache] performSelector:@selector(diskImageDataBySearchingAllPathsForKey:) withObject:URL.absoluteString]; image = [UIImage imageWithData:data]; } if(image) { return image.size; } }#endif NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL]; NSString* pathExtendsion = [URL.pathExtension lowercaseString]; CGSize size = CGSizeZero; if([pathExtendsion isEqualToString:@"png"]){ size = [self getPNGImageSizeWithRequest:request]; } else if([pathExtendsion isEqual:@"gif"]) { size = [self getGIFImageSizeWithRequest:request]; } else{ size = [self getJPGImageSizeWithRequest:request]; } if(CGSizeEqualToSize(CGSizeZero, size)) { // 如果获取文件头信息失败,发送异步请求请求原图 NSData* data = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:URL] returningResponse:nil error:nil]; UIImage* image = [UIImage imageWithData:data]; if(image) {//如果未使用SDWebImage,则忽略;缓存该图片#ifdef dispatch_main_sync_safe [[SDImageCache sharedImageCache] storeImage:image recalculateFromImage:YES imageData:data forKey:URL.absoluteString toDisk:YES];#endif size = image.size; } }//如果对加载速度及用户体验要求不高的话,可以通过主线程获取图片大小//会阻塞主线程,慎重使用!!!if (CGSizeEqualToSize(CGSizeZero, size)) { //直接获取图片大小 NSData *data = [NSData dataWithContentsOfURL:URL]; UIImage *image = [UIImage imageWithData:data]; size = image.size;} return size;}// 获取PNG图片的大小+(CGSize)getPNGImageSizeWithRequest:(NSMutableURLRequest*)request{ [request setValue:@"bytes=16-23" forHTTPHeaderField:@"Range"]; NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; if(data.length == 8) { int w1 = 0, w2 = 0, w3 = 0, w4 = 0; [data getBytes:&w1 range:NSMakeRange(0, 1)]; [data getBytes:&w2 range:NSMakeRange(1, 1)]; [data getBytes:&w3 range:NSMakeRange(2, 1)]; [data getBytes:&w4 range:NSMakeRange(3, 1)]; int w = (w1 << 24) + (w2 << 16) + (w3 << 8) + w4; int h1 = 0, h2 = 0, h3 = 0, h4 = 0; [data getBytes:&h1 range:NSMakeRange(4, 1)]; [data getBytes:&h2 range:NSMakeRange(5, 1)]; [data getBytes:&h3 range:NSMakeRange(6, 1)]; [data getBytes:&h4 range:NSMakeRange(7, 1)]; int h = (h1 << 24) + (h2 << 16) + (h3 << 8) + h4; return CGSizeMake(w, h); } return CGSizeZero;}// 获取GIF图片的大小+(CGSize)getGIFImageSizeWithRequest:(NSMutableURLRequest*)request{ [request setValue:@"bytes=6-9" forHTTPHeaderField:@"Range"]; NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; if(data.length == 4) { short w1 = 0, w2 = 0; [data getBytes:&w1 range:NSMakeRange(0, 1)]; [data getBytes:&w2 range:NSMakeRange(1, 1)]; short w = w1 + (w2 << 8); short h1 = 0, h2 = 0; [data getBytes:&h1 range:NSMakeRange(2, 1)]; [data getBytes:&h2 range:NSMakeRange(3, 1)]; short h = h1 + (h2 << 8); return CGSizeMake(w, h); } return CGSizeZero;}// 获取JPG图片的大小+(CGSize)getJPGImageSizeWithRequest:(NSMutableURLRequest*)request{ [request setValue:@"bytes=0-209" forHTTPHeaderField:@"Range"]; NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; if ([data length] <= 0x58) { return CGSizeZero; } if ([data length] < 210) {// 肯定只有一个DQT字段 short w1 = 0, w2 = 0; [data getBytes:&w1 range:NSMakeRange(0x60, 0x1)]; [data getBytes:&w2 range:NSMakeRange(0x61, 0x1)]; short w = (w1 << 8) + w2; short h1 = 0, h2 = 0; [data getBytes:&h1 range:NSMakeRange(0x5e, 0x1)]; [data getBytes:&h2 range:NSMakeRange(0x5f, 0x1)]; short h = (h1 << 8) + h2; return CGSizeMake(w, h); } else { short word = 0x0; [data getBytes:&word range:NSMakeRange(0x15, 0x1)]; if (word == 0xdb) { [data getBytes:&word range:NSMakeRange(0x5a, 0x1)]; if (word == 0xdb) {// 两个DQT字段 short w1 = 0, w2 = 0; [data getBytes:&w1 range:NSMakeRange(0xa5, 0x1)]; [data getBytes:&w2 range:NSMakeRange(0xa6, 0x1)]; short w = (w1 << 8) + w2; short h1 = 0, h2 = 0; [data getBytes:&h1 range:NSMakeRange(0xa3, 0x1)]; [data getBytes:&h2 range:NSMakeRange(0xa4, 0x1)]; short h = (h1 << 8) + h2; return CGSizeMake(w, h); } else {// 一个DQT字段 short w1 = 0, w2 = 0; [data getBytes:&w1 range:NSMakeRange(0x60, 0x1)]; [data getBytes:&w2 range:NSMakeRange(0x61, 0x1)]; short w = (w1 << 8) + w2; short h1 = 0, h2 = 0; [data getBytes:&h1 range:NSMakeRange(0x5e, 0x1)]; [data getBytes:&h2 range:NSMakeRange(0x5f, 0x1)]; short h = (h1 << 8) + h2; return CGSizeMake(w, h); } } else { return CGSizeZero; } }}@end
PS:
png和gif格式的图片头文件固定,可以准确找到宽高所在字节,只需要极少流量就可以获得图片大小。但是jpeg的头文件很混乱,我真的想说它根本就不分header和body,绘图软件编辑一次保存一次就添加一个标记码,然后存放宽高的标记码就消失在数据流的某个地方了,因为不知道有多少个绘图软件编辑过,也不知道绘图软件标记码里面的内容格式,所以手动获取jpeg图片的宽高基本不可能了。
不过博主偶然发现了一个方法,大家可以尝试一下:
就是在图片url地址后面拼接参数@100p,利用获取JPG格式图片的方法即可获得JPEG图片的大小了。
if (![imgUrl containsString:@"@"]) {
imgUrl = [imgUrl stringByAppendingString:@"@100p"];
}
参考:
http://bbs.itheima.com/thread-158741-1-1.html
http://www.oschina.net/code/snippet_2248391_53038
http://www.2cto.com/kf/201405/304877.html
http://www.cocoachina.com/bbs/read.php?tid=455783