I'm developing an iOS 4 application with iOS 5.0 SDK and XCode 4.2.
I have to show some post blogs into a UITableView. When I have retreived all web service data, I use this method to create an UITableViewCell:
- (BlogTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString* cellIdentifier = @"BlogCell"; BlogTableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if (cell == nil) { NSArray* topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BlogTableViewCell" owner:nil options:nil]; for(id currentObject in topLevelObjects) { if ([currentObject isKindOfClass:[BlogTableViewCell class]]) { cell = (BlogTableViewCell *)currentObject; break; } } } BlogEntry* entry = [blogEntries objectAtIndex:indexPath.row]; cell.title.text = entry.title; cell.text.text = entry.text; cell.photo.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:entry.photo]]]; return cell; }
But this line:
cell.photo.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:entry.photo]]];
it is so slow (entry.photo has a http url).
Is there any way to load that image asynchronously? I think it is difficult because tableView:cellForRowAtIndexPath is called very often.
It is, but it isn't possible to store an image as is in the user's defaults database. The defaults system only supports strings, numbers, Date objects, and Data objects. This means that you need to convert the image to a Data object before you can store it in the user's defaults database.
UIImage contains the data for an image. UIImageView is a custom view meant to display the UIImage .
I wrote a custom class to do just this, using blocks and GCD:
WebImageOperations.h
#import <Foundation/Foundation.h> @interface WebImageOperations : NSObject { } // This takes in a string and imagedata object and returns imagedata processed on a background thread + (void)processImageDataWithURLString:(NSString *)urlString andBlock:(void (^)(NSData *imageData))processImage; @end
WebImageOperations.m
#import "WebImageOperations.h" #import <QuartzCore/QuartzCore.h> @implementation WebImageOperations + (void)processImageDataWithURLString:(NSString *)urlString andBlock:(void (^)(NSData *imageData))processImage { NSURL *url = [NSURL URLWithString:urlString]; dispatch_queue_t callerQueue = dispatch_get_current_queue(); dispatch_queue_t downloadQueue = dispatch_queue_create("com.myapp.processsmagequeue", NULL); dispatch_async(downloadQueue, ^{ NSData * imageData = [NSData dataWithContentsOfURL:url]; dispatch_async(callerQueue, ^{ processImage(imageData); }); }); dispatch_release(downloadQueue); } @end
And in your ViewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // Pass along the URL to the image (or change it if you are loading there locally) [WebImageOperations processImageDataWithURLString:entry.photo andBlock:^(NSData *imageData) { if (self.view.window) { UIImage *image = [UIImage imageWithData:imageData]; cell.photo.image = image; } }]; }
It is very fast and will load the images without affecting the UI or scrolling speed of the TableView.
*** Note - This example assumes ARC is being used. If not, you will need to manage your own releases on objects)
In iOS 6 and later dispatch_get_current_queue gives deprecation warnings.
Here is an alternative that is a synthesis of the @ElJay answer above and the article by @khanlou here.
Create a category on UIImage:
UIImage+Helpers.h
@interface UIImage (Helpers) + (void) loadFromURL: (NSURL*) url callback:(void (^)(UIImage *image))callback; @end
UIImage+Helpers.m
#import "UIImage+Helpers.h" @implementation UIImage (Helpers) + (void) loadFromURL: (NSURL*) url callback:(void (^)(UIImage *image))callback { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); dispatch_async(queue, ^{ NSData * imageData = [NSData dataWithContentsOfURL:url]; dispatch_async(dispatch_get_main_queue(), ^{ UIImage *image = [UIImage imageWithData:imageData]; callback(image); }); }); } @end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With