2

I need to read images from NSDocumentDirectory to multiple uiimageview async so it won't block the UI. I know i can use perform selector in background to load a uiimage, but then how can i associate it with the dynamic uiimageview ?

3 Answers3

4

One convenient way is to use blocks, something like:

[self loadFullImageAt:imagePath completion:^(UIIMage * image){
    self.imageView.image = image;
}];

Where you would load the image as data (since UIImage otherwise loads the image data deferred - when you first access it). It's also a good idea to decompress the image while still in the background thread, so the main thread doesn't have to do it when we first use the image.

- (void)loadFullImageAt:(NSString *)imageFilePath completion:(MBLoaderCompletion)completion {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        NSData *imageData = [NSData dataWithContentsOfFile:imageFilePath];
        UIImage *image = nil;
        if (imageData) {
            image = [[[UIImage alloc] initWithData:imageData] decodedImage];
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            completion(image);
        });
    });
}

The callback is defined as:

typedef void (^MBLoaderCompletion)(UIImage *image);

Here's an UIImage category that implements the decompression code:

UIIMage+Decode.h

#import <UIKit/UIKit.h>


@interface UIImage (Decode)

- (UIImage *)decodedImage;

@end

UIIMage+Decode.m

#import "UIImage+Decode.h"


@implementation UIImage (Decode)

- (UIImage *)decodedImage {
    CGImageRef imageRef = self.CGImage;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL,
                                                 CGImageGetWidth(imageRef),
                                                 CGImageGetHeight(imageRef),
                                                 8,
                                                 // Just always return width * 4 will be enough
                                                 CGImageGetWidth(imageRef) * 4,
                                                 // System only supports RGB, set explicitly
                                                 colorSpace,
                                                 // Makes system don't need to do extra conversion when displayed.
                                                 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
    CGColorSpaceRelease(colorSpace);
    if (!context) return nil;

    CGRect rect = (CGRect){CGPointZero,{CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)}};
    CGContextDrawImage(context, rect, imageRef);
    CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context);
    CGContextRelease(context);

    UIImage *decompressedImage = [[UIImage alloc] initWithCGImage:decompressedImageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(decompressedImageRef);
    return decompressedImage;
}

@end

The sample code provided here assumes that we're using ARC.

iOS Dev
  • 4,143
  • 5
  • 30
  • 58
Matej Bukovinski
  • 6,152
  • 1
  • 36
  • 36
2

When you say "dynamic" UIImageView, are these programmatically created on a UIScrollView? on a UITableView? samfisher is quite right on the basic question, but the details differ a little based upon how you created the UIImageView (e.g. if UITableView, you need to make sure that the cell is still visible and hasn't been dequeued; if UIScrollView, even then you might want to only load the UIImageView if the image is still visible on the screen (esp if the images are large or numerous)).

But the basic idea is that you might do something like:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    UIImage *image = [self getTheImage];

    // ok, now that you have the image, dispatch the update of the UI back to the main queue

    dispatch_async(dispatch_get_main_queue(), ^{

        // if the image view is still visible, update it

    });

});

Note that you invoke the retrieval of the image on some background queue or thread, but make sure to update the UI back on the main thread!

If you're updating a scrollview, you might want to do some checking that the view is still visible, such as contemplated here or here. If you're updating a tableview, perhaps something like this which checks if the cell is still visible. It all depends upon what you're trying to do.

Community
  • 1
  • 1
Rob
  • 415,655
  • 72
  • 787
  • 1,044
1

you can use NSThread/dispatch queue for creating threads which can create your UIImageView-s and loads up images in them.

Saurabh Passolia
  • 8,099
  • 1
  • 26
  • 41