2

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.

CalZone
  • 1,555
  • 1
  • 14
  • 28
  • 1
    "App crashes" is a very bad explanation. Can you provide crash logs and error messages? Your app could be crashing by **a lot** of different reasons. – Bruno Koga Jun 01 '13 at 13:57
  • A number of things wrong with your code there, but before that - are you sure Assets library is thread-safe? – Mar0ux Jun 01 '13 at 14:38
  • `@property (nonatomic, readwrite) ALAssetsLibrary *assetsLibrary;` and `@property (nonatomic, readwrite) ALAssetsGroup *assetGroup;` – CalZone Jun 01 '13 at 14:40

2 Answers2

2

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.

edzio27
  • 4,048
  • 6
  • 29
  • 48
  • Performance is a little better, but cells are not being loaded, they load only when I start scrolling. – CalZone Jun 01 '13 at 14:17
  • Have you try to reload cell after set the image? [self.tableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationFade]; – edzio27 Jun 01 '13 at 14:23
  • 1
    Do you think reason for this might be the fact that `cellforrowAtIndexPath` is called before `viewDidload` ? and since I'm making group in `viewdidload`, its not available for `cellForRowAtIndexPath` – CalZone Jun 01 '13 at 15:01
2

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.

Community
  • 1
  • 1
Kedar
  • 1,288
  • 10
  • 19