views:

485

answers:

4

I have some large images in a grouped UITableView. There's 1 row in each section which holds the image and there is associated header and footer text for each section about the image. I worked hard to get it to scroll smoothly is iPhone OS 3.x and finally succeeded. But... when I upgraded to iOS 4, that all changed. Now there is very jerky performance whenever a new cell is loaded. Here is my cellForRowAtIndexPath code:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"CellIdentifier";

    UIImageView *photo;
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    ApplicationDelegate *appDelegate = (ApplicationDelegate *)[[UIApplication sharedApplication] delegate];

    NSString *fileName = [NSString stringWithFormat:@"%@",[[appDelegate.sectionsDelegateDict objectAtIndex:indexPath.section] objectForKey:@"MainTrackImage"]];
    UIImage *theImage = [UIImage imageNamed:[[appDelegate.sectionsDelegateDict objectAtIndex:indexPath.section] objectForKey:@"MainTrackImage"]];

    imageHeight = CGImageGetHeight(theImage.CGImage);
    imageWidth = CGImageGetWidth(theImage.CGImage);

    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
        photo = [[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, (imageHeight*320)/imageWidth)] autorelease];
        photo.tag = PHOTO_TAG;
        [cell addSubview:photo];
    } else {
        photo = (UIImageView *) [cell viewWithTag:PHOTO_TAG];
        [photo setFrame:CGRectMake(0, 0, 320, (imageHeight*320)/imageWidth)];
    }

    photo.image = theImage;
    return cell;
}

Note: I've tried using the following code in place of the "imageNamed" method with no success:

UIImage *theImage = [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/%@", bundlePath,fileName]];

I've even tried pre-caching the images but with no success. Any help would be greatly appreciated.

UPDATE: I've tried experimenting with using Grand Central Dispatch to load the images by creating an image queue and loading them asynchronously:

dispatch_async(_image_queue, ^{
photoImageView.image = [imageCacheNS objectForKey:imageString];});

The problem I'm getting is that the images load very slowly and when I scroll the table, the previously viewed images are shown for a few seconds until the new image is loaded.

UPDATE 2: I still have not been able to make this work. I'm now looking into using the drawRect method to draw the cell. I'm hoping this will improve scrolling performance. Apple's tableViewSuite sample code example #5 shows how to do this, but I'm having trouble finding out how to make this work for my project. Anyone have any experience using drawRect: to loading images into a tableview?

Thanks!

+1  A: 

Try loading the image in another thread. This will help improve the responsiveness of your table view (and other UI elements). This is a place where blocks and libdispatch would be excellent additions to your code.

Jeff Kelley
Do you care to elaborate? I'm not familiar with using blocks or libdispatch. Do you know of any examples where blocks and libdispatch are used in UITableViews? Thanks!
Jonah
See here:http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/Blocks/Articles/00_Introduction.htmlAlso, if you're a registered Apple developer, you can download the WWDC 2010 videos for free. The blocks and Grand Central Dispatch (libdispatch) videos are fantastic.
Jeff Kelley
Thanks for the help. I'll take a look.
Jonah
Do you mean something like this? I added this to my cellForRowAtIndexPath method: dispatch_queue_t image_queue; image_queue = dispatch_queue_create("com.test.queue", NULL); dispatch_async(image_queue, ^{photoImageView.image = theImage;}); dispatch_release(image_queue);
Jonah
Sort of, but one thing you should never do is update an image view (or any other UI element) on anything but the main thread. Load your image from disk/network in a separate queue, then use the main thread's queue to change the image view. You can call the main thread from inside your block to make it easier.
Jeff Kelley
+1  A: 

The LazyTableImages sample code from Apple might help. http://developer.apple.com/iphone/library/samplecode/LazyTableImages/Introduction/Intro.html

Shows you how to load the images without blocking the UI.

Dad
Have you ever heard of anyone using lazy loading to add images that are stored locally?
Jonah
sure! If the list is long or the images are large (e.g., you don't have separate thumbnails). The purpose is the same - take the loading hit out of the main UI thread to increase responsiveness. Generally you'd use a single shared placeholder image that you load once at the beginning so there is _something_ there while the other is loading.
Dad
+1  A: 

re your: "The problem I'm getting is that the images load very slowly and when I scroll the table, the previously viewed images are shown for a few seconds until the new image is loaded." Bet that's because the image cell is being re-used but in the case where it's not nil you don't set the image property to nil to clear the old one out. Just a guess since your code is obviously now different than what is posted above.

BTW - I haven't used blocks yet in part because they require iOS 4 (unless you use non-apple compilers, i.e., PLBlocks) and I've been writing apps to still support 3.x. Just in case you were only building against 4.x now and might not realize the code you were writing was going to be a no-go on iOS 3.x (e.g., iPad for time being). Clearly you can do conditional code depending on the os version at runtime and so on.

Dad
A: 

After a huge amount of work and several communications with Apple dev support, I finally got this working. I am now loading very large images in a tableview in the background and have very fast scrolling performance.

Basically, I followed parts of the code from WWDC 2010 sample code "CoreAnimationImageBrowser". It's designed for the iPad and is based around a custom scrollView instead of a tableView, but the ImageScrollView class is the one to use to load your images.

Then, the really weird thing I had to do to account for the fact that the cells can't be preloaded in a tableView was to add the images as subviews BEHIND the tableView. As the table is scrolled, the images are already loaded and are quick to load.

Sort of a weird hack, but the Apple Tech seemed to think it was fine.

Good luck to anyone trying to do this. It took quite a bit of time for me to get it figured out.

Jonah