Search code examples
iosobjective-cuitableviewautolayout

Reload constraint or update height in self-sizing tableview cell after image is downloaded


I show 1 image in my cell and height need to be changed according to aspect ratio. But if I don't reload table, constraint is not changed and thus cell height too. If I use 'reloadData' or reload particular row, it is okay but scrolling performance is affected. One of the ways is to download all images first but might not be good I guess. How shall I do?

Media *media = self.post.medias[0];
NSString *imgUrlStr = media.thumbnailUrl;
[self.ivMain allowTapWithMedia:self.post.medias[0]];

UIImage* displayImage = [myCache imageFromDiskCacheForKey:imgUrlStr];

if (displayImage) {
    @try {

        [self.ivMain setImage:displayImage];
        CGFloat ivHeight = (displayImage.size.height/displayImage.size.width) * CGRectGetWidth(self.ivMain.frame);

        if (roundf(ivHeight) != roundf(self.verticalConstraintIvMain.constant))
            [self.verticalConstraintIvMain setConstant:ivHeight];

    } @catch (NSException *exception) {

    }
}
else {
    [self.ivMain setImageWithURL:[NSURL URLWithString:imgUrlStr] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {

        //image width > iv width
        //image height .. ?

        dispatch_async(dispatch_get_main_queue(), ^{
            CGFloat ivHeight = (image.size.height/image.size.width) * CGRectGetWidth(self.ivMain.frame);
            if (roundf(self.verticalConstraintIvMain.constant) != roundf(ivHeight)) {

                // [self.verticalConstraintIvMain setConstant:ivHeight];

                id view = [self superview];

                while (view && [view isKindOfClass:[UITableView class]] == NO) {
                    view = [view superview];
                }

                UITableView *tableView = (UITableView *)view;
                [tableView reloadData];
                // [tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.tag inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
            }
        });
    } usingActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];


}

Solution

  • The known solution is to not reload rows while the user is scrolling. It's impossible to have major layout operations and smooth scrolling simultaneously.

    So when the async image-download call comes back, detect if the user is scrolling by looking at the table view properties (inherited from UIScrollView) isDragging and isDecelerating. If the user is scrolling, instead put the 'index path to reload' into some storage. A mutable array property on whatever is the table view data source/delegate would be best, for reasons below. When the user stops scrolling, reload the paths and clear the storage.

    You can do the last step by implementing the UIScrollViewDelegate method -scrollViewDidEndDecelerating:. The table view's delegate is also the scroll view's delegate. It can have a nice effect in practice to see multiple images appear when scrolling stops.