Question

I'm trying to populate images from camera roll in a tableview.

in ViewDidLoad, I create an asset group.

- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;

self.assetsLibrary = [[ALAssetsLibrary alloc] init];
[self.assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
    if(nil!=group){
        [group setAssetsFilter:[ALAssetsFilter allPhotos]];
        self.assetGroup = group;
        NSLog(@"%d images found", self.assetGroup.numberOfAssets);
    }
} failureBlock:^(NSError *error) {
    NSLog(@"block fucked!");
}];
[self.tableView reloadData];

}

in CellForRowAtIndexPath, I use GCD background queue to populate cells with image at respective index.

 static NSString *CellIdentifier = @"Cell";

    dispatch_queue_t imgLoadQueue = dispatch_queue_create("Thumb loader", NULL);
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

}

dispatch_async(imgLoadQueue, ^{
    [self.assetGroup enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:indexPath.row] options:0 usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
        if (nil != result) {
            ALAssetRepresentation *repr = [result defaultRepresentation];
            UIImage *img = [UIImage imageWithCGImage:[repr fullResolutionImage]];
            cell.imageView.image = img;

        }
    }];

});


return cell;

The problem is, initial cells are empty. Images start loading only after I start scrolling. And that too, app crashes as soon as I scroll. I'm fairly new to GCD, and don't seem to be using it correctly. Any help in the matter is appreciated.

Was it helpful?

Solution

You need to call [self.tableView reloadData] after the enumeration block in viewDidLoad gets completed and self.assetGroup has been populated. The enumeration block is executed asynchronously, therefore the reloadData is being called before the block is completed and on the table view delegate callbacks your assetGroup contains no data. By the time you start scrolling, the property is populated and you start seeing the images.

I have not seen apple documentation that explains how to detect end of enumeration block, but these two accepted answers indicate that the group value will be nil when the enumeration is over.

iPhone enumerateGroupsWithTypes finishing selector Find out when my asynchronous call is finished

So put an else condition in your group enumeration block -

 if(nil!=group){
    [group setAssetsFilter:[ALAssetsFilter allPhotos]];
    self.assetGroup = group;
    NSLog(@"%d images found", self.assetGroup.numberOfAssets);
}
else
    [self.tableView reloadData];

Remove the reloadData being called after the enumeration block.

Try taking your enumeration block in CellForRowAtIndexPath out of the GCD queue. That block will execute asynchronously too. No need to dispatch it to a background queue.

OTHER TIPS

Change line in your cellforRowAtIndexPath method from:

cell.imageView.image = img;

to:

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

Every change in views must be implemented in main thread, if you change your cell image in other thread (what you current do) cell presentation wont change.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top