The question doesn't specify the desired behavior for varying image sizes. Should the cell get taller to fit, or just the image view (what looks like a button from the code) within the cell?
But we should put aside that problem for a moment and work on the more serious problem with the code: it issues an unguarded network request from cellForRowAtIndexPath
. As a result, (a) a user scrolling back and forth will generate many many redundant requests, and (b) a user scrolling a long way quickly will generate a request that's fulfilled when the cell that started it is gone - reused as the cell for another row.
To address (a), the datasource should cache fetched images, and only request those that haven't been received. To address (b), the completion block shouldn't refer directly to the cell.
A simple cache would look like this:
@property(strong,nonatomic) NSMutableDictionary *images;
// initialize this when you initialize your model
self.images = [@{} mutableCopy];
// move the network code into its own method for clarity
- (void)imageWithPath:(NSString *)path completion:(void (^)(UIImage *, NSError *))completion {
if (self.images[indexPath]) {
return completion(self.images[indexPath], nil);
}
NSURL *imageURL = [NSURL URLWithString:path];
NSURLRequest *request = [NSURLRequest requestWithURL:imageURL];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!error){
UIImage *image = [UIImage imageWithData:data];
self.images[indexPath] = image;
completion(image, nil);
} else {
completion(nil, error);
}
}];
}
Now, we fix the multiple request problem by checking first for the image in the cache in cellForRowAtIndexPath.
UIImage *image = self.images[indexPath];
if (image) {
[cell.CellImg setBackgroundImage:image forState:UIControlStateNormal];
} else {
// this is a good place for a placeholder image if you want one
[cell.CellImg setBackgroundImage:nil forState:UIControlStateNormal];
// presuming that 'img' is a string from your mode
[self imageWithPath:img completion:^(UIImage *image, NSError *error) {
// the image is ready, but don't assign it to the cell's subview
// just reload here, so we get the right cell for the indexPath
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}];
}
Also notice what isn't done in the completion block... we're fixing the cell reuse by not referring to the cell. Instead, knowing that the image is now cached, we reload the indexPath.
Back to image sizing: most apps like to see the table view cell get taller or shorter along with the variable height subview. If that's the case, then you should not place a height constraint on that subview at all. Instead, constrain it's top and bottom edges to the cell's content view (or include it in a chain of subviews who constrain to each other top and bottom and contain the topmost and bottommost subviews top and bottom edge to the cell). Then (in iOS 5+, I think), this will allow your cell to change hight with that subview constraint chain...
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = // your best guess at the average height here