简体   繁体   中英

iOS 5: UITableView cells are not scrolling smooth

Using:

  • iOS 5, ARC, StoryBoards.

Have 1 image and text on every cell. Images are not getting resized while scrolling. Have two versions of images image.png and image@2x.png.

The custom cell was managed by drugging UIImageView and UILabel in storyboard.

Code:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
Continent *continent=[self.items objectAtIndex:[indexPath row]];
ContinentCell *cell = (ContinentCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    cell.continentName.text=continent.continentName;
    cell.textView.text=continent.countriesHash;
    cell.imageView.image=[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:continent.continentImage ofType:@"png"]];
 return cell;
}

Where's the evil? Thank you in advance.

[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:continent.continentImage ofType:@"png"]];

Is expensive. You want to load the image the image asynchronously in the background then present it on the main thread when ready.

Here's a very rough solution

    cell.imageView.image = nil;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      UIImage * img = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:continent.continentImage ofType:@"png"]];
      dispatch_sync(dispatch_get_main_queue(), ^{
        cell.imageView.image = img;
      });
    });

Check out Polishing your App: Tips and Tricks to Improve Responsiveness and Performance video of WWDC 2011 from 26:48 . They have discussed exactly about the problem you are facing. Don't miss out, it would be really helpful..!

Make sure that your images are not big and then:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";
    Continent *continent=[self.items objectAtIndex:[indexPath row]];
    ContinentCell *cell = (ContinentCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if(cell == nil) cell = [[ContinentCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]
    cell.continentName.text = continent.continentName;
    cell.textView.text = continent.countriesHash;
    cell.imageView.image = [UIImage imageNamed:continent.continentImage];
    return cell;
}

I made 2 significant changes:

  1. Added if(cell == nil) [[ContinentCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] , because I didn't find the code for initializing cell (unless you use -registerNib or the other super-sectet function, which I can't say because it's under NDA)

  2. Replaced imageWithContentsOfFile:... with simple imageNamed , because you load the image from your main bundle and, if image is not big, -imageNamed caches it, so it loads quicker. (and -imageNamed doesn't need the file extension)

- (UITableViewCell *)tableView:(UITableView *)tableView 
                                 cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    Continent *continent=[self.items objectAtIndex:[indexPath row]];
    ContinentCell *cell = (ContinentCell*)[tableView 
                               dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[ContinentCell alloc] 
                               initWithStyle:UITableViewCellStyleDefault 
                               reuseIdentifier:CellIdentifier];
     }
    cell.continentName.text=continent.continentName;
    cell.textView.text=continent.countriesHash;
    //cell.imageView.image=[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] 
                        pathForResource:continent.continentImage ofType:@"png"]];
    cell.imageView.image = [UIImage cachedImage:continent.continentImage];
    return cell;
}

I will recommend you to use imageNamed instead of imageWithContentsOfFile.

imageNamed method loads the image in cache and next time it will load from cache, where as imageWithContentsOfFile method loads the image from your specified path without NO cahching and it will create multiple copy in memory.

You can create your own image cache method. Just declare NSMutableDictionary *imagedCacheDict

If you run out of memory you can remove all the objects by [imagedCacheDict removeAllObjects]

 - (UIImage*)cachedImage:(NSString*)fileName
{
   UIImage *cacheImage = [imagedCacheDict objectForKey:fileName];

   if (nil == cacheImage)
   {
      NSString *cacheImageFile = [NSString stringWithFormat:@"%@.png", 
                                   [[NSBundle mainBundle] resourcePath], fileName];
      cacheImage = [UIImage imageWithContentsOfFile:cacheImageFile];
      [imagedCacheDict setObject:cacheImage forKey:fileName];
   }
   return cacheImage;
}

So, never use the imageNamed method, it will bring out your application by consuming lot of memory.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM