1

I'm adding an image from a web service. How would I code this so that it's not on the main thread? I want the view to load first and then the image to load so that the user doesn't experience any slowness when the detail view controller loads.

What I'm unsure of is how to add this into the dispatching code.

here's my code so far:

   NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:mainImageUrl]];
UIImage *image = [[UIImage alloc] initWithData:imageData];

UIView *v = [[UIView alloc] initWithFrame:CGRectMake(10, 150, 300, 180)];
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(0, 10, 300, 180)];
[iv setImage:image];

[_scrollView addSubview:v];
[v addSubview:iv];

and this is what I'm thinking I can use for the threading:

dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Add code here to do background processing
//
//
dispatch_async( dispatch_get_main_queue(), ^{
    // Add code here to update the UI/send notifications based on the
    // results of the background processing
});

});

thanks for the help

user2588945
  • 1,681
  • 6
  • 25
  • 38
  • check below answer it will help you http://stackoverflow.com/a/15377082/1713478 – Pratik Aug 07 '13 at 09:25
  • You can safely use your code to download the image data in the background and then update the UI on the main thread. What is your concern? – Vik Aug 07 '13 at 09:22
  • So, in my viewDidLoad method I would just add the dispatch code and put the image data download code within the dispatch_async( dispatch_get_main_queue(), ^{ }) block? and then the rest of the viewDidLoad code in the dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ } block? Is that right? thanks – user2588945 Aug 07 '13 at 09:25

7 Answers7

2

You can use the GCD to download image like this.

//Your ImageView
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(0, 10, 300, 180)];
// Create a __block type reference for the variable, for ARC use __weak instead
__block UIImageView *blockimage = iv;

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,  0ul);
dispatch_async(queue, ^{
    NSData *imageData=[NSData dataWithContentsOfURL:[NSURL URLWithString:mainImageUrl]];
    dispatch_sync(dispatch_get_main_queue(), ^{
        blockimage.image = [UIImage imageWithData:imageData];
        // To avoid retain cycle
        blockimage = nil;
    });
});

This code is downloading the image synchronously within GCD async block by using dataWithContentsOfURL: API, disadvantage is you cannot get the precise information when image fails to download. In this case to properly handle error situations, use NSURLConnection to asynchronously download the image data.

Also, as suggested correctly in few of answers to your question, you can use SDWebImage or AFNetworking image view categories. This will give you image cache facility and ensure that the image is downloaded asynchronously without affecting the main thread performance.

Hope that helps!

Amar
  • 13,202
  • 7
  • 53
  • 71
1

Or you could use the AFNetworking category on UIImageView to do that !

iSofTom
  • 1,718
  • 11
  • 15
1

Use SDWebImage to download image on secondary thread. It will load image in UIImageView on sencondary thread. Import SDWebImage package and use this code

here is the code

#import "SDWebImage/UIImageView+WebCache.h"

[yourimageView setImageWithURL:[NSURL URLWithString:@"your image Url"] placeholderImage:[UIImage imageNamed:@""]];
chandan
  • 2,453
  • 23
  • 31
1
 NSURLRequest *req = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.hdwallpapersbest.com/wp-content/uploads/2012/12/Beauty-of-Nature-Awesome-Images-01-21.jpg"]];
    [NSURLConnection sendAsynchronousRequest:req queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *res, NSData *data, NSError *err) {

        [_imageView setImage:[UIImage imageWithData:data]];

    }];
aahsanali
  • 3,537
  • 23
  • 21
0

Create NSOperationQueue and create NSOperation.You will have to make a nsmutableurl request using NSOperation.It will start image downloading in background.After your work is finished you have to move to main thread using performselector on mainthread.

[super viewDidLoad];
    // Create a new NSOperationQueue instance.
    NSPerationQueue *operationQueue = [NSOperationQueue new];
    // Create a new NSOperation object using the NSInvocationOperation subclass.
    // Tell it to run the counterTask method.
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
                                                                selector:@selector(counterTask)
                                                                object:nil];
    // Add the operation to the queue and let it to be executed.
    [operationQueue addOperation:operation];
    [operation release];
    // The same story as above, just tell here to execute the colorRotatorTask method.
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
                                                    selector:@selector(colorRotatorTask)
                                                    object:nil];
    [operationQueue addOperation:operation];
    [operation release];
NHS
  • 409
  • 2
  • 7
  • Good suggestion, but I think GCD would be a cleaner solution. Also, NSOperationQueue uses GCD under covers. – Amar Aug 07 '13 at 09:43
  • yes you are right.But you should use NSOperation to maintain a list of threads in one queue and its so easy syntactically. – NHS Aug 07 '13 at 09:48
0

I would do as suggested run you code using dispatch_async setting the variables to use a background thread. Then use [self performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO]; to update the UI on the main thread

geminiCoder
  • 2,918
  • 2
  • 29
  • 50
0

The code you have proposed looks like the correct way to do it using GCD. However, in this specific case (since you're loading a URL) I suggest you take a look at NSURLRequest and NSURLConnection because these classes do it for you and provide a good framework for setting up caching policies, handling errors, etc.

Here is some example code:

NSURLRequest* request = [NSURLRequest requestWithURL:yourNSURLObject];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse* response, NSData* data, NSError* error)
{
    // The data is now available in the 'data' object.
    UIImage* image = [UIImage imageWithData:data];
    // Do whatever with the image.
}];
jhabbott
  • 18,461
  • 9
  • 58
  • 95