Here is the UIImage category that I use for this. It is based on fastimage . One of the main warnings is that the NSURLConnectionDelegate is set to NSURL itself, and this can lead to collisions in certain situations. This is not complete yet (the file url is ignored, for example ...), but you can see where it goes.
Title:
#import <UIKit/UIKit.h>
typedef void (^UIImageSizeRequestCompleted) (NSURL* imgURL, CGSize size);
@interface UIImage (RemoteSize)
+ (void) requestSizeFor: (NSURL*) imgURL completion: (UIImageSizeRequestCompleted) completion;
@end
Source:
#import "UIImage+RemoteSize.h"
#import <objc/runtime.h>
static char *kSizeRequestDataKey = "NSURL.sizeRequestData";
static char *kSizeRequestTypeKey = "NSURL.sizeRequestType";
static char *kSizeRequestCompletionKey = "NSURL.sizeRequestCompletion";
typedef uint32_t dword;
@interface NSURL (RemoteSize)
@property (nonatomic, strong) NSMutableData* sizeRequestData;
@property (nonatomic, strong) NSString* sizeRequestType;
@property (nonatomic, copy) UIImageSizeRequestCompleted sizeRequestCompletion;
@end
@implementation NSURL (RemoteSize)
- (void) setSizeRequestCompletion: (UIImageSizeRequestCompleted) block {
objc_setAssociatedObject(self, &kSizeRequestCompletionKey, block, OBJC_ASSOCIATION_COPY);
}
- (UIImageSizeRequestCompleted) sizeRequestCompletion {
return objc_getAssociatedObject(self, &kSizeRequestCompletionKey);
}
- (void) setSizeRequestData:(NSMutableData *)sizeRequestData {
objc_setAssociatedObject(self, &kSizeRequestDataKey, sizeRequestData, OBJC_ASSOCIATION_RETAIN);
}
- (NSMutableData*) sizeRequestData {
return objc_getAssociatedObject(self, &kSizeRequestDataKey);
}
- (void) setSizeRequestType:(NSString *)sizeRequestType {
objc_setAssociatedObject(self, &kSizeRequestTypeKey, sizeRequestType, OBJC_ASSOCIATION_RETAIN);
}
- (NSString*) sizeRequestType {
return objc_getAssociatedObject(self, &kSizeRequestTypeKey);
}
#pragma mark - NSURLConnectionDelegate
- (void) connection: (NSURLConnection*) connection didReceiveResponse:(NSURLResponse *)response {
[self.sizeRequestData setLength: 0];
}
- (void) connection: (NSURLConnection*) connection didReceiveData:(NSData *)data {
NSMutableData* receivedData = self.sizeRequestData;
if( !receivedData ) {
receivedData = [NSMutableData data];
self.sizeRequestData = receivedData;
}
[receivedData appendData: data];
const unsigned char* cString = [receivedData bytes];
const NSInteger length = [receivedData length];
const char pngSignature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
const char bmpSignature[2] = {66, 77};
const char gifSignature[2] = {71, 73};
const char jpgSignature[2] = {255, 216};
if(!self.sizeRequestType ) {
if( memcmp(pngSignature, cString, 8) == 0 ) {
self.sizeRequestType = @"PNG";
}
else if( memcmp(bmpSignature, cString, 2) == 0 ) {
self.sizeRequestType = @"BMP";
}
else if( memcmp(jpgSignature, cString, 2) == 0 ) {
self.sizeRequestType = @"JPG";
}
else if( memcmp(gifSignature, cString, 2) == 0 ) {
self.sizeRequestType = @"GIF";
}
}
if( [self.sizeRequestType isEqualToString: @"PNG"] ) {
char type[5];
int offset = 8;
dword chunkSize = 0;
int chunkSizeSize = sizeof(chunkSize);
if( offset+chunkSizeSize > length )
return;
memcpy(&chunkSize, cString+offset, chunkSizeSize);
chunkSize = OSSwapInt32(chunkSize);
offset += chunkSizeSize;
if( offset + chunkSize > length )
return;
memcpy(&type, cString+offset, 4); type[4]='\0';
offset += 4;
if( strcmp(type, "IHDR") == 0 ) {
dword width = 0, height = 0;
memcpy(&width, cString+offset, 4);
offset += 4;
width = OSSwapInt32(width);
memcpy(&height, cString+offset, 4);
offset += 4;
height = OSSwapInt32(height);
if( self.sizeRequestCompletion ) {
self.sizeRequestCompletion(self, CGSizeMake(width, height));
}
self.sizeRequestCompletion = nil;
[connection cancel];
}
}
else if( [self.sizeRequestType isEqualToString: @"BMP"] ) {
int offset = 18;
dword width = 0, height = 0;
memcpy(&width, cString+offset, 4);
offset += 4;
memcpy(&height, cString+offset, 4);
offset += 4;
if( self.sizeRequestCompletion ) {
self.sizeRequestCompletion(self, CGSizeMake(width, height));
}
self.sizeRequestCompletion = nil;
[connection cancel];
}
else if( [self.sizeRequestType isEqualToString: @"JPG"] ) {
int offset = 4;
dword block_length = cString[offset]*256 + cString[offset+1];
while (offset<length) {
offset += block_length;
if( offset >= length )
break;
if( cString[offset] != 0xFF )
break;
if( cString[offset+1] == 0xC0 ||
cString[offset+1] == 0xC1 ||
cString[offset+1] == 0xC2 ||
cString[offset+1] == 0xC3 ||
cString[offset+1] == 0xC5 ||
cString[offset+1] == 0xC6 ||
cString[offset+1] == 0xC7 ||
cString[offset+1] == 0xC9 ||
cString[offset+1] == 0xCA ||
cString[offset+1] == 0xCB ||
cString[offset+1] == 0xCD ||
cString[offset+1] == 0xCE ||
cString[offset+1] == 0xCF ) {
dword width = 0, height = 0;
height = cString[offset+5]*256 + cString[offset+6];
width = cString[offset+7]*256 + cString[offset+8];
if( self.sizeRequestCompletion ) {
self.sizeRequestCompletion(self, CGSizeMake(width, height));
}
self.sizeRequestCompletion = nil;
[connection cancel];
}
else {
offset += 2;
block_length = cString[offset]*256 + cString[offset+1];
}
}
}
else if( [self.sizeRequestType isEqualToString: @"GIF"] ) {
int offset = 6;
dword width = 0, height = 0;
memcpy(&width, cString+offset, 2);
offset += 2;
memcpy(&height, cString+offset, 2);
offset += 2;
if( self.sizeRequestCompletion ) {
self.sizeRequestCompletion(self, CGSizeMake(width, height));
}
self.sizeRequestCompletion = nil;
[connection cancel];
}
}
- (void) connection: (NSURLConnection*) connection didFailWithError:(NSError *)error {
if( self.sizeRequestCompletion )
self.sizeRequestCompletion(self, CGSizeZero);
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
return cachedResponse;
}
- (void) connectionDidFinishLoading: (NSURLConnection *)connection {
if(!self.sizeRequestData.length) {
self.sizeRequestData = nil;
}
else {
UIImage* image = [UIImage imageWithData: self.sizeRequestData];
if( self.sizeRequestCompletion && image) {
self.sizeRequestCompletion(self, [image size]);
return;
}
}
self.sizeRequestCompletion(self, CGSizeZero);
}
@end
@implementation UIImage (RemoteSize)
+ (void) requestSizeFor: (NSURL*) imgURL completion: (UIImageSizeRequestCompleted) completion {
if( [imgURL isFileURL] ) {
}
else {
imgURL.sizeRequestCompletion = completion;
NSURLRequest* request = [NSURLRequest requestWithURL: imgURL];
NSURLConnection* conn = [NSURLConnection connectionWithRequest: request delegate: imgURL];
[conn scheduleInRunLoop: [NSRunLoop mainRunLoop] forMode: NSDefaultRunLoopMode];
[conn start];
}
}
@end