Hoang Aloevera Hoang Aloevera - 11 months ago 49
iOS Question

How to improve load image in UITableview?

I have a problem when i load imageview in table view. It's Loading so slowly. I use a lot of section in my table view. There is my code:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TrangChuTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellTrangchu" forIndexPath:indexPath];

theGame *thegameObj;
if([indexPath section] == 0){

thegameObj = [theZingArray objectAtIndex:indexPath.row];

}else if([indexPath section] == 1){

thegameObj = [theBitArray objectAtIndex:indexPath.row];

}else if([indexPath section] == 2){

thegameObj = [theMobayArray objectAtIndex:indexPath.row];

}else if([indexPath section] == 3){

thegameObj = [theGateArray objectAtIndex:indexPath.row];

}else if([indexPath section] == 4){

thegameObj = [theVcoinArray objectAtIndex:indexPath.row];

}else if([indexPath section] == 5){

thegameObj = [theGarenaArray objectAtIndex:indexPath.row];

}else if([indexPath section] == 6){

thegameObj = [theOncashArray objectAtIndex:indexPath.row];

}else if([indexPath section] == 7){

thegameObj = [theMobiphoneArray objectAtIndex:indexPath.row];

}else if([indexPath section] == 8){

thegameObj = [theVinaphoneArray objectAtIndex:indexPath.row];

}else if([indexPath section] == 9){

thegameObj = [theViettelArray objectAtIndex:indexPath.row];

NSURL *urlImage = [NSURL URLWithString:thegameObj.image];
NSData *imageData = [NSData dataWithContentsOfURL:urlImage];
UIImage *image= [UIImage imageWithData:imageData];
cell.imageView.image = image;
cell.labelName.text = thegameObj.objectName;

if([[AppDelegate appDelegate]checkIP])
[cell.txtQuantity setBackgroundColor:[UIColor whiteColor]];
[cell.txtQuantity.layer setBorderColor:[UIColor grayColor].CGColor];
[cell.txtQuantity.layer setBorderWidth:1.0];
[cell.txtQuantity.layer setCornerRadius:5];
cell.labelPrice.text = [NSString stringWithFormat:@"%@ (%@)", [NSNumberFormatter localizedStringFromNumber:thegameObj.price numberStyle:NSNumberFormatterDecimalStyle], loadCurrency];

[cell.lbdetail setTitle:@"detail" forState:UIControlStateNormal];


cell.objArray = thegameObj;
cell.currency = loadCurrency;
[cell.addToCartButton addTarget:self action:@selector(addToCart:) forControlEvents:UIControlEventTouchUpInside];
cell.addToCartButton.tag = [NSString stringWithFormat:@"%d_%d", (NSInteger)[indexPath section], (NSInteger)[indexPath row]];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

return cell;

Please help me improve more faster load image with alot of section in UItableview. Thank you for any solution!

Answer Source

One thing that will help is a data structure that matches a sectioned table model, which is an array of arrays. You're almost there, since you have all of the sub-arrays. Build one like this (when all the sub-arrays are built, not in cellForRow):

NSArray *model = @[ theZingArray, theBitArray, /*... and so on */];

That will let you flatten the big conditional to one line:

thegameObj = model[indexPath.section][indexPath.row];

You can use that elsewhere, like in numberRowsInSection

NSArray *sectionArray = model[indexPath.section];
return sectionArray.count;

The slightly tougher problem is to have those images load async and get cached. A fully native approach is discussed here and many others.

Applying this idea to your code, here's a method that returns either a cached image, or fetches, caches and returns...

// declare this and BE SURE TO INITIALIZE IT
@property(strong,nonatomic) NSMutableDictionary *imageCache;

// return an image found at the url string, if one is cached return it, otherwise,
// fetch it, cache it and return it
- (void)imageAtURLString:(NSString *)urlString completion:(void(^)(UIImage *, BOOL))completion {
    if (self.imageCache[urlString]) {  // if it's cached
        completion(self.imageCache[urlString], YES);
    } else {  // fetch and cahce
        NSURL *url = [NSURL URLWithString:urlString];

        NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            if (data) {
                UIImage *image = [UIImage imageWithData:data];
                if (image) {
                    self.imageCache[urlString] = image;
                    dispatch_async(dispatch_get_main_queue(), ^{
                        completion(image, NO);
        [task resume];

Now your simplified cellForRowAtIndexPath can look like this:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellid"];  // fixme

    theGame *thegameObj = model[indexPath.section][indexPath.row];
    NSString *urlString = thegameObj.image;
    UIImageView *imageView = cell.imageView;

    [self imageAtURLString:urlString completion:^(UIImage *image, BOOL wasCached) {
        if (wasCached) imageView.image = image;
        else [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

    return cell;

The idea here is that you either have a cached image, in which case, just set the cell's imageView image to that, or you had to fetch one. In the case of the fetch, it's uncertain whether the row got scrolled away during the fetch. Reload the current row to cause it to update with the (now cached) image.