0

I'm building my custom cell for a table view. I'm trying to load an image from internet and for it, i'm using async download. The image is being downloaded, but it's not showing this image in my cell. I already tried to show in a normal view and it's working fine. It does work too if the image is already downloaded or if I roll the table view and show the cell again. Does anybody knows what's going on?

Code:

DownloadImageManager.m

-(id)initWithImageName:(NSString *)imageAddress{
    self = [super initWithFrame:CGRectMake(10, 5, 100, 100)];
    if (self){
        self.urlString = imageAddress;
        av = [[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge] autorelease];
        av.frame = self.frame;
        [av setBackgroundColor:[UIColor greenColor]];
        [self addSubview:av];
        [av startAnimating];
        [self checkImage];
    }
    return self;
}

-(void)checkImage{
    bool isImageOnSysten = [self isImageOnFileSystem];
    if (isImageOnSysten) {
        //If image is on the system, loads the image, it's working fine here
        NSLog(@"CSantos: isImageOnSysten %@ is on system", self.urlString);
    } else {
        //here is the problem:
        [self downloadImage];
    }
}

-(void)downloadImage{
    NSURL *url = [NSURL URLWithString:self.urlString];
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    [request setDelegate:self];
    [request setAllowCompressedResponse:YES];
    [request setQueuePriority:NSOperationQueuePriorityLow];
    [request setDidFinishSelector:@selector(requestFinished:)];
    [request setDidFailSelector:@selector(requestFailed:)];
    [request setTimeOutSeconds:25];
    [request setNumberOfTimesToRetryOnTimeout:3];

    [request startAsynchronous];
}

- (void)requestFinished:(ASIHTTPRequest *)request
{
    NSData *responseData = [request responseData]; 
    NSArray *words = [self.urlString componentsSeparatedByString:@"/"];
    NSString *fileName = [words lastObject];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
    NSString *documentsDirectory = [paths objectAtIndex:0]; 
    NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:fileName]; 
    NSError *error = nil;
    [responseData writeToFile:writablePath options:NSDataWritingAtomic error:&error];
    NSLog(@"Write returned error: %@", [error localizedDescription]);
    [av stopAnimating];
    [av removeFromSuperview];
}

CellForProgram.m

- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
    if (self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) {

        textLabel = [[UILabel alloc]initWithFrame:CGRectMake(60, 31, 235, 40)] ;
        [self.contentView addSubview:textLabel];

        photo = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, 70, 70)];
        [photo setBackgroundColor:[UIColor blueColor]];
        photo.image = imagePhoto.image;
        [self.contentView addSubview:photo];
    }
    return self

Cell Caller

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";

    CellForProgram *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier] ;
    if (cell == nil) {
        cell = [[[CellForProgram alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
    }

    cell.textLabel.text = [speaker objectAtIndex:indexPath.row];

    DownloadImageManager *imageManager = [[DownloadImageManager alloc] initWithImageName:[images objectAtIndex:indexPath.row]];
    [cell.photo setImage:imageManager.image];
    return cell;
}
Rupesh Yadav
  • 12,096
  • 4
  • 53
  • 70
Claudio
  • 1,987
  • 3
  • 29
  • 55

2 Answers2

1

You're not working with the pointers correctly.

When you call [cell.photo setImage:imageManager.image]; and the image does not exists, you're pointing it to nil or to an random memory space.

You need to create a pointer to your cell on the DownloadImageManager class, so that you can update the cell when the image finishes downloading.

Here's what I recommend:

  • Create a property on DownloadImageManager that points to your custom UITableViewCell class
  • Do not set the image on the tableView:cellForRowAtIndexPath: selector. Instead, set it directly on the DownloadImageManager.

Here's a simple modification to your code:

Cell Caller

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";

    CellForProgram *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier] ;
    if (cell == nil) {
        cell = [[[CellForProgram alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
    }

    cell.textLabel.text = [speaker objectAtIndex:indexPath.row];

    DownloadImageManager *imageManager = [[DownloadImageManager alloc] initWithImageName:[images objectAtIndex:indexPath.row] andCell:cell];
    return cell;
}

DownloadImageManager.m

-(id)initWithImageName:(NSString *)imageAddress andCell:(CellForProgram*)cell{
    self = [super initWithFrame:CGRectMake(10, 5, 100, 100)];
    if (self){
        self.urlString = imageAddress;
        self.cell = cell;
        av = [[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge] autorelease];
        av.frame = self.frame;
        [av setBackgroundColor:[UIColor greenColor]];
        [self addSubview:av];
        [av startAnimating];
        [self checkImage];
    }
    return self;
}

-(void)checkImage{
    bool isImageOnSysten = [self isImageOnFileSystem];
    if (isImageOnSysten) {
        //If image is on the system, loads the image, it's working fine here
        NSLog(@"CSantos: isImageOnSysten %@ is on system", self.urlString);
        cell.photo = self.image;
    } else {
        //here is the problem:
        [self downloadImage];
    }
}

-(void)downloadImage{
    NSURL *url = [NSURL URLWithString:self.urlString];
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    [request setDelegate:self];
    [request setAllowCompressedResponse:YES];
    [request setQueuePriority:NSOperationQueuePriorityLow];
    [request setDidFinishSelector:@selector(requestFinished:)];
    [request setDidFailSelector:@selector(requestFailed:)];
    [request setTimeOutSeconds:25];
    [request setNumberOfTimesToRetryOnTimeout:3];

    [request startAsynchronous];
}

- (void)requestFinished:(ASIHTTPRequest *)request
{
    NSData *responseData = [request responseData]; 
    NSArray *words = [self.urlString componentsSeparatedByString:@"/"];
    NSString *fileName = [words lastObject];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
    NSString *documentsDirectory = [paths objectAtIndex:0]; 
    NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:fileName]; 
    NSError *error = nil;
    [responseData writeToFile:writablePath options:NSDataWritingAtomic error:&error];
    NSLog(@"Write returned error: %@", [error localizedDescription]);
    [av stopAnimating];
    [av removeFromSuperview];

    cell.photo = self.image;
}

That should get you going. If you need any clarification, be sure to leave a comment and I'll answer shortly.

EDIT: As an alternative, implement an delegate method on the DownloadImageManager...

Add this to the DownloadImageManager.h:

@protocol DownloadImageManagerDelegate <NSObject>
@optional
- (void)DownloadFinished:(DownloadImageManager*)manager;
@end

Instead of the CellForProgram, use the DownloadImageManager protocol, with this constructor as example:

-(id)initWithImageName:(NSString *)imageAddress andDelegate:(DownloadImageManagerDelegate*)delegate

And change your implementation of requestFinished: like so:

- (void)requestFinished:(ASIHTTPRequest *)request
{
    NSData *responseData = [request responseData]; 
    NSArray *words = [self.urlString componentsSeparatedByString:@"/"];
    NSString *fileName = [words lastObject];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 
    NSString *documentsDirectory = [paths objectAtIndex:0]; 
    NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:fileName]; 
    NSError *error = nil;
    [responseData writeToFile:writablePath options:NSDataWritingAtomic error:&error];
    NSLog(@"Write returned error: %@", [error localizedDescription]);
    [av stopAnimating];
    [av removeFromSuperview];

    if ([delegate respondsToSelector:@selector(DownloadFinished:)]) {
      [delegate DownloadFinished:self];
    }
}

Then, make your cell implment the given protocol, like so:

- (void)DownloadFinished:(DownloadImageManager*)manager {
  this.photo = manager.image;
}

This way you can keep your functionality on DownloadImageManager, as you want it.

Sergio Moura
  • 4,888
  • 1
  • 21
  • 38
  • Mate, this is not really a good idea. This is changing DownloadImageManager at a point that I just can use in this cell. If I try to use anywhere else, I will get a lot of error messages. – Claudio Nov 16 '11 at 15:04
  • @Claudio i've added to the answer an protocol/delegate idea, that you can use to keep the functionality of your download class, and still solve your problem. – Sergio Moura Nov 16 '11 at 16:39
  • I've just said: DownloadImageManager is working in all other interfaces but that one (cell/tableview). It's not a problem in this class, but the way i'm using here. – Claudio Nov 17 '11 at 17:12
0

I told I wouldn't need to do this kind of change on DownloadImageManager! But thanks for trying to help, it helped me in other stuff I was stucked!

CellForProgram.m

- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
    if (self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) {

        textLabel = [[UILabel alloc]initWithFrame:CGRectMake(60, 31, 235, 40)] ;
        [self.contentView addSubview:textLabel];

        imagePhoto = [[DownloadImageManager alloc] initWithImageName:imageAdress.text];
        [self.contentView addSubview:imagePhoto];
    }
    return self
}

DownLoadImageManager.m: add this method

-(void)changeImage:(NSString *)newImage{
    self.urlString = newImage;
    [self checkImage];
}

Cell Caller

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";

    CellForProgram *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier] ;
    if (cell == nil) {
        cell = [[[CellForProgram alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
    }

    cell.textLabel.text = [speaker objectAtIndex:indexPath.row];

    [cell.imagePhoto changeImage:[images objectAtIndex:indexPath.row]];
    return cell;
}
Claudio
  • 1,987
  • 3
  • 29
  • 55