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

| | August 5, 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

2 Responses to “Transfering code from cellForItemAtIndexPath to a CollectionViewCell (Parse Back-End)”

  1. Vikas Dadheech on November 30, -0001 @ 12:00 AM

    create a method in your custom cell which is exposed in your .h file.

    This method should receive an argument of type PFObject.

    Then in you cellForItemAtIndexPath, call that method and pass your object in that method.

    And in the implementation of that method, extract the details from your object and assign them to respective properties.

  2. 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