Transfering code from cellForItemAtIndexPath to a CollectionViewCell (Parse Back-End)

| | August 4, 2015

I’m using Parse as the database for my app. I want to create a CollectionViewCell and transfer my code there, instead of having it inside the View Controller’s cellForItemAtIndexPath. How do I do this?

Thanks.

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *identifier = @"productCell";

    ProductCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];

    PFObject *product = [self.products objectAtIndex:indexPath.row];

    NSString *price = [NSString stringWithFormat:@"$%@.00", product[@"price"]];

    cell.price.text = price;

    PFFile *userImageFile = product[@"firstThumbnailFile"];
    [userImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
        if (!error) {
            UIImage *thumbnailImage = [UIImage imageWithData:imageData];
            UIImageView *thumbnailImageView = [[UIImageView alloc] initWithImage:thumbnailImage];

            cell.image.image = thumbnailImageView.image;
        }
    }];

    return cell;
}

Cell.h

@interface ProductCell : UICollectionViewCell

@property (nonatomic, weak) IBOutlet UIImageView *image;
@property (nonatomic, weak) IBOutlet UILabel *price;

@end

One Response to “Transfering code from cellForItemAtIndexPath to a CollectionViewCell (Parse Back-End)”

  1. Remember that cellForIndexPath is called over and over as cells scroll into view. So it’s bad practice to make unguarded network requests in that method.

    If you want to fetch the images lazily, add logic that caches the retrieved results, and only fetch images that haven’t been fetched before…

    // in private interface
    @property(strong,nonatomic) NSMutableDictionary *imageForProduct;
    
    // in init
    self.imageForProduct = [@{} mutableCopy];
    

    A method to fetch an image…

    - (void)imageForProduct:(PFObject *)product completion:(void (^)(UIImage *))completion {
        PFFile *userImageFile = product[@"firstThumbnailFile"];
        [userImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
            UIImage *image;
            if (!error) {
                image = [UIImage imageWithData:imageData];
            }
            completion(image);
        }];
    }
    

    Now, in cellForIndexPath, we can’t count on the state of the collection being the same by the time the image arrives, so rather than retaining manipulating the cell in the completion block, just reload the index path…

    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
        static NSString *identifier = @"productCell";
        ProductCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
    
        PFObject *product = [self.products objectAtIndex:indexPath.row];
        NSString *price = [NSString stringWithFormat:@"$%@.00", product[@"price"]];
        cell.price.text = price;
    
        if (self.imageForProduct[product.objectId]) {
            cell.image = self.imageForProduct[product.objectId];
        } else {
            cell.image = // optionally put a placeholder image here
            [self imageForProduct:product completion:^(UIImage *)image {
                self.imageForProduct[product.objectId] = image;
                [collectionView reloadItemsAtIndexPaths:@[indexPath]];
            }];
        }
        return cell;
    }
    

Leave a Reply