17

I'm building a UICollectionView and my custom cells will contain two labels and one image.

Each image is downloaded asynchronously so I don't know it's size until the download it's complete. Once is downloaded, I want to adapt each cell to re-layout it's content and frame to fit in height the image just downloaded.

As UICollectionViewLayout, I'm using CHTCollectionViewWaterfallLayout

To download the image asynchronously I'm using SDWebImage, like this:

    [cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
                   placeholderImage:[UIImage imageNamed:@"placeholder.png"]
                          completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) 
                                {... some completion code here ...}];

QUESTION:

What is the right approach to resize each UICollectionViewCell right after the image is downloaded?

Fmessina
  • 3,751
  • 3
  • 31
  • 34

3 Answers3

28

You should just be able to invalidate your collection view layout in an animation block when the image comes back. Things might get a little complicated if more than one image finishes completion at once, but this should work:

[cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
               placeholderImage:[UIImage imageNamed:@"placeholder.png"]
                      completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType)^{
                          [UIView animateWithDuration:0.3f animations:^{
                              [self.collectionView.collectionViewLayout invalidateLayout];
                          }];
                      }];

Then just return a different size in the appropriate delegate method.

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    return /* a different size if the image is done downloading yet */;
}
Ash Furrow
  • 12,391
  • 3
  • 57
  • 92
  • 1
    Thanks! I'm getting a perfect result the first time the UICollectionView is loaded, but when it's loaded for the second time (i.e. moving to another view and coming back) I get weird cell frame sizes (smaller than expected, with content overlapping). If I scroll a bit, everything is displayed properly again. Do I have to change the cell frame size (and where) after the image is downloaded? – Fmessina Apr 22 '14 at 19:49
  • 2
    Solved. Actually there was nothing do nothing with the cell frame. I just had to set the cell.imageView frame just before the animateWithDuration block. `[cell.imageView setFrame:CGRectMake(2,2,image.size.width,image.size.height])]; [UIView animateWithDuration:0.3f animations:^{ [self.collectionView.collectionViewLayout invalidateLayout]; }];` – Fmessina Apr 22 '14 at 21:07
  • Should I be calling it this way with a callback because if I do I have an issue since it expects a return expression, since I have it called on each cell? http://stackoverflow.com/questions/23316634/how-to-cache-image-and-scale-it-correctly That is how I wrote it, and was actually trying the resize but it does not keep the aspect ratio and instead cuts out a piece of the image? – Lion789 Apr 26 '14 at 21:53
  • What did you return in size sizeForItemAtIndexPath method? – Waqar Khalid Sep 07 '18 at 11:01
1

To resize collection view cells, you could reload the collection view and return the size of you collection view cells dynamically in the method:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
    return [cell size];
}

In cases when the cell's size is dynamic, get the cell from the index path and return its size based on the size of the image. I usually create a method for the cell to return a dynamic size, like in the example above. You can use UIImage's size property to help return the size based on the image.

Chris
  • 1,663
  • 1
  • 15
  • 19
  • When should I reload the collection view, after all the images are downloaded? Or it's better to reload each cell with reloadItemsAtIndexPaths? – Fmessina Apr 17 '14 at 12:01
  • Doing it in this way you would have to reload the collection view after downloading all of the images. – Chris Apr 22 '14 at 21:24
  • +1 because even if your answer didn't solve my issue, I used the idea of a [cell size] method. However @Ash solution was what I was looking for. – Fmessina Apr 22 '14 at 22:01
  • You should not call dequeueReusableCellWithReuseIdentifier: multiple times without returning the cell to the collection, e.g. in cellForItemAtIndexPath:. The collection will have hundreds of dequeued cells sitting around that are never returned to be reused. – Matt Oct 14 '14 at 03:17
  • @Matt, thanks for pointing that out! I updated the answer to use `cellForItemAtIndexPath:`. – Chris Oct 14 '14 at 17:47
0

You could try to reloadItemsAtIndexPaths: for the cells that finished loading.

Rivera
  • 10,792
  • 3
  • 58
  • 102