UITableViewCell height resize when image is downloaded

13,370

You need to store downloaded image in the memory or on the disk, so next time when you will try to get image from this URL you will received from cache.

So if you do that than you will have to do something like this:

[tableView beginUpdates];

[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];

[tableView endUpdates];

And you should return new cell's height in this method of table view data source:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

I would recomend you to use SDWebImage library instead of AFNetworking because it can cache your images to memcache and disk for you and it is very easy to use. So if you decide to use it, your code of download images will be looking like this:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    ...

    [cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
           placeholderImage:[UIImage imageNamed:@"placeholder.png"]
                    success:^(UIImage *image, BOOL cached) {

                        // save height of an image to some cache
                        [self.heightsCache setObject:[NSNumber numberWithFloat:imHeight] 
                                              forKey:urlKey];

                        [tableView beginUpdates];
                        [tableView reloadRowsAtIndexPaths:@[indexPath]
                                         withRowAnimation:UITableViewRowAnimationFade];
                        [tableView endUpdates];
                    }
                    failure:^(NSError *error) {... failure code here ...}];

}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    // try to get image height from your own heights cache
    // if its is not there return default one
    CGFloat height = [[self.heightsCache objectForKey:urlKeyFromModelsArrayForThisCell] floatValue];
    ...
    return newHeight;
}
Share:
13,370
Jure
Author by

Jure

Programmer and petrolhead.

Updated on June 03, 2022

Comments

  • Jure
    Jure about 2 years

    I'm using UIImageView+AFNetworking category for async image loading. Everything works fine, but I've tried a couple of things and wasn't successful when resizing the cell's height according to the downloaded image. I want the image to fit to the width of the cell but resize in height, but I don't know how to accomplish that. I've tried reloading the row where the image was downloaded but that just causes the cellForRowAtIndexPath to fire again and set everything again, etc. Almost a recursion.

    I'm calculating the new image size difference and storing that in NSMutableArray and then reloading the row in the success block in UIImageView's setImageWithURLRequest:placeholderImage:success:.

    I get the correct height for a couple of rows in heightForRowAtIndexPath but then, the table starts acting weird and everything is overlaying, etc.

    Any ideas?

    Thanks!

    EDIT

    I eventually used Ezeki's answer. I also wrote a post on the topic and made a sample app that uses this technique.

    Check it out on here.

  • Jure
    Jure over 11 years
    Thanks, that's a great suggestion! Btw, AFNetworking handles caching as well. Anyway, I got it working but the tableview scrolling is now very jerky because of the reloading. Maybe I'll try preloading the images with SDWebImagePrecacher. I have to find out a method of calculating the cell height because I have other subviews in the cell as well.
  • Rajan Balana
    Rajan Balana about 11 years
    @Jure Did you found the complete solution to the problem? I am going through same kind of problem. Can you help?
  • Surjit Sidhu
    Surjit Sidhu over 10 years
    where self.heightsCache property is declared ?
  • Ezeki
    Ezeki over 10 years
    that's just an NSMutableDictionary property, you can declare in in your ViewController.h file or in ViewController.m using category extension. Don't forget to initialize it in init or viewDidLoad. This mutable dictionary takes a url string as a key and NSNumber as a value.
  • Kosmetika
    Kosmetika about 10 years
    @Ezeki I tried to implement my tableview in this manner but as I see there are several minuses: 1. Memory - a lot of memory is needed for big lists while doing this calls [tableView beginUpdates]; [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; [tableView endUpdates]; (I saw nearly 30mb for my list) 2. It's not so smooth
  • Ezeki
    Ezeki about 10 years
    @Kosmetika you can try reloading the table without row animation, maybe that would help. Anyway, please let me know the solution which really helps in your case
  • Mil0R3
    Mil0R3 about 10 years
    @Ezeki This solution make tableView always reloading data in a loop, I log the self.heightsCache in tableView:heightForRowAtIndexPath, the console is keep printing the log.
  • Ezeki
    Ezeki about 10 years
    @Geaka tableView:heightForRowAtIndexPath: is always called when new cell appears on the screen or when we reload the data. So If you scroll the tableView - yes, it would be always called, but if you don't touch it - it should call this method only when cell is reloading
  • Joan Cardona
    Joan Cardona about 8 years
    I keep getting this error: The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (0), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). anybody knows how to fix it?
  • Jonny
    Jonny over 6 years
    Looks promising - did anyone write this for Swift yet?