Wasim
Wasim

Reputation: 5113

Using NSURLConnection to get data in UIImageVIew category; can't set image property

I've created a category for UIImageView to allow for async loading of images:

@implementation UIImageView (ImageLoader)

- (void) asyncLoadImageIntoView:(NSURL *)imageURL withLoadingIndicator:(BOOL)isLoadingIndicator {

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:imageURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];

    (void)[[NSURLConnection alloc] initWithRequest:request delegate:self];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 
{
   // responseData = [[NSMutableData alloc] init];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{
    NSLog(@"didReceiveData %@", data);
    (void)[self.image initWithData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 
{

//[textView setString:@"Unable to fetch data"];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
{
    NSLog(@"Succeeded! Received image data");
}



@end

My issue is that even though the connection:didReceiveData: method is called and it prints the data to the console, the [self.image initWithData:data] does not seem to be loading the image into the view. What is the problem here?

Upvotes: 1

Views: 1198

Answers (4)

jscs
jscs

Reputation: 64002

First, mehdzor's right; connection:didRecieveData: doesn't mean that you've got all the data; you need to wait until you get connectionDidFinishLoading: to be sure. If the image is very large, for example, it may not all come at once.

You should create an NSMutableData instance when you open the connection, and add to it in connection:didRecieveData::

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{
    NSLog(@"didReceiveData %@", data);
    [[self mutableData] appendData:data]
}

Since this is a category, you will probably have to use the associated objects functions to store and access that mutable data instance.

Second, [self.image initWithData:data] doesn't make any sense. You can't initialize an object until you've allocated it, and you can't send init... to an object that's already been initialized.

The better way, though, since you're going to assign this image to a property, is to use UIImage's convenience method imageWithData:. Again, you need to do this in connectionDidFinishLoading:, and you will pass your mutable data instance, which now has all the data that the connection managed to pull down:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
{
    NSLog(@"Succeeded! Received image data");
    [self setImage:[UIImage imageWithData:[self mutableData]]];
}

Don't forget to clean up (release) the mutableData instance after this.

Upvotes: 5

clearscreen
clearscreen

Reputation: 2027

There are at least two mistakes in your code.

The first mistake is that you should not be calling [self.image initWithData] as all init methods are meant to be called once, and during construction of an object, right after the alloc:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{
    NSLog(@"didReceiveData %@", data);
    (void)[self.image initWithData:data]; // Wrong...
}

The second mistake is that the method above is incremental, and can be called multiple times before the full image is received. What you want to do instead is store your data in a NSMutableData object (or similar) until all data has been received, and create an image once you get the message:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
   // Create an image with your NSMutableData object here
}

You will probably need to use something other than a category to do this properly, as you'd probably need new properties.

Upvotes: 4

Wasim
Wasim

Reputation: 5113

I have fixed the issue. I'm not exactly sure why, but creating a UIImage from the data first, then assigning the image of the UIImageView to the UIImage worked.

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{
    UIImage *img = [[UIImage alloc] initWithData:data];
    self.image = img;
}

Upvotes: 0

Mehdzor
Mehdzor

Reputation: 799

I think your problem is that you create image every time you receive part of it. You should gather all data in one piece and than create an image;

Upvotes: 0

Related Questions