views:

801

answers:

3

Hi guys,

I was wondering if anyone knew the best way of getting in a UITableView, where we have images on the left hand side, for the images to appear with UIActivity indicator gradually rather than not being there and appearing all of a sudden. Ive seen that on a few apps and wanted to know how to do it.

+1  A: 

If I can remember correctly something like that was shown in stanford lectures cs193, iPhone Application Programming, when loading profile pictures from twitter accounts.

Have a look for the appropriate presentation (I can't remember which one it was), look at notes that talk about table view then have a look at itunesu video of the lecture.

http://www.stanford.edu/class/cs193p/cgi-bin/index.php

stefanB
+1  A: 

I haven't done this myself, but my idea would be to create my own UIView subclass that handles showing the UIActivity indicator while the image is loading. Then add instances of that view to your table cells instead of plain UIImageViews.

Update: OK, as I said before, I haven't done this myself. The following is just typed into the browser, and I'm not sure if you'd run into any issues with threading, but it should give you an idea as to how to approach this:

@interface IndicatorImageView : UIView {
    UIImageView *imageView;
    UIActivityIndicatorView *indicator;
    NSString *imageName;
    NSURL *imageURL;
}
@property (nonatomic, copy) NSString *imageName;
@property (nonatomic, retain) NSURL* imageURL;
@end

@implementation IndicatorImageView
@synthesize imageName;
@synthesize imageURL;
- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
     imageView = [[UIImageView alloc] initWithFrame:self.bounds];
     [self addSubview:imageView];

     indicator = [[UIActivityIndicatorView alloc] initWithFrame:self.bounds];
     [self addSubview:indicator];
    }
    return self;
}
- (void)setImageName:(NSString *)anImageName {
    if (imageName == anImageName) {
     return;
    }
    [imageName release];
    imageName = [anImageName copy];

    // optionally remove the old image while we're updating
    //imageView.image = nil;

    [indicator startAnimating];
    [self performSelectorInBackground:@selector(updateImageViewUsingImageName) withObject:nil];
} 
- (void)setImageURL:(NSURL *)anImageURL {
    if (imageURL == anImageURL) {
     return;
    }
    [imageURL release];
    imageURL = [anImageURL copy];

    // optionally remove the old image while we're updating
    //imageView.image = nil;

    [indicator startAnimating];
    [self performSelectorInBackground:@selector(updateImageViewUsingImageURL) withObject:nil];
} 
- (void)updateImageViewUsingImageName {
    NSAutoreleasepool *pool = [[NSAutoreleasePool alloc] init];
    imageView.image = [UIImage imageNamed:imageName];
    [indicator performSelectorOnMainThread:@selector(stopAnimating) withObject:nil];
    [pool release];
}
- (void)updateImageViewUsingImageURL {
    NSAutoreleasepool *pool = [[NSAutoreleasePool alloc] init];
    NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
    imageView.image = [UIImage imageWithData:imageData];
    [indicator performSelectorOnMainThread:@selector(stopAnimating) withObject:nil];
    [pool release];
}
@end

When you set the imageName property, the image will be loaded in the background while the UIActivityIndicatorView is animating. Once the image is loaded and set on the UIImageView, we stop animating the activity indicator.

Thomas Müller
I've added some code. HTH
Thomas Müller
thanks your'e a champ mate
Doron Katz
Not sure how i assign cell.imageView.(image?) to this subclass Tim
Doron Katz
You'll need to add an instance of IndicatorImageView to the cell, and then set the image name: IndicatorImageView *indicatorImageView = [[IndicatorImageView alloc] initWithFrame:frame]; [cell addSubview:indicatorImageView]; indicatorImageView.imageName = @"theImageName"; If you need to load the image from a URL or file path, just adjust the class accordingly so that you can set the URL or file path instead of an image name.
Thomas Müller
Ive tried doing it that way THomas, and keep on getting autoreleased with no pool in place - just leaking. Is the IndicatorImageView leaking? or do I need to put an AutoReleasePool somewhere?
Doron Katz
Maybe try creating an AutoReleasePool at the start of -updateImageView and then release it at the end of that method. That method will run in a different thread, so it wouldn't be using the normal AutoReleasePool created in the Runloop.
Thomas Müller
awesome that worked fine. Sorry, im not very familiar with autoreleasepool, but ill take this opportunity to read up on it. Cheers
Doron Katz
Hi Thomas, one thing though, before, I had my images stored in coredata as actual transformables, now having them as strings to URLs, the code you gave me with the method setName is for local file locations. ive got an NSURL object, how do i associate it with that? Thx
Doron Katz
Never mind, ive completed this by changing your code above to accept an UIImage rather than a string, therefore i can make use of coredata and it's store of transformable UIImage. Thanks Thomas
Doron Katz
I've updated the code so you can load the image from a URL. You could remove the imageName stuff from the above if you don't need it.
Thomas Müller
+1  A: 

Subclass UIImage and add the UIActivityIndicator as a subview. Add some methods to the class for setting the image source, and start/stop the UIActivityIndicator appropriately.

Mike Weller