Peter Peter - 5 months ago 38
iOS Question

Using dispatch_async to load images in UICollectionView

I'm using Parse to store images. I am trying to load the images asynchronously so it does not interfere with the

collectionView
scroll. I am new to using
dispatch_async
, and am not so sure how to properly implement it. I've also looked into lazy load, but I thought this would work. But it doesn't, and the
collectionView
scroll is choppy. Thanks

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
albumImageCell *cell = (albumImageCell *) [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];

if (cell == nil) {
cell = [[albumImageCell alloc]init];
}

PFObject *temp = [_dataArray objectAtIndex:indexPath.row];
PFFile *file = [temp objectForKey:@"image"];

if (cell.hasImage == FALSE) {
dispatch_async(imageQueue, ^{
NSData *data = [file getData];
if (data) {
dispatch_async(dispatch_get_main_queue(), ^(void){
cell.imageView.image = [UIImage imageWithData:data];
cell.hasImage = TRUE;
});

}
});
}

return cell;
}

Answer

Because UIImage +imageWithData method blocks the main thread a bit.

dispatch_async(dispatch_get_main_queue(), ^(void){
    cell.imageView.image = [UIImage imageWithData:data];

So these lines should be as the following.

UIImage *image = [UIImage imageWithData:data];

dispatch_async(dispatch_get_main_queue(), ^(void){
    cell.imageView.image = image;

Besides, UIImage doesn't decode the image immediately till the UIImage object is actually shown or rendered. See http://stackoverflow.com/a/19251240/629118. Thus, the following code is one of the best way to get rid of choppy scrolling.

UIImage *image = [UIImage imageWithData:data];

UIGraphicsBeginImageContext(CGSizeMake(1,1));
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawImage(context, CGRectMake(0, 0, 1, 1), [image CGImage]);
UIGraphicsEndImageContext();

dispatch_async(dispatch_get_main_queue(), ^(void){
    cell.imageView.image = image;