I am trying to asynchronously download images for a UITableViewCell, but it is currently setting the same image to each cell.
Please can you tell me the problem with my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
SearchObject *so = (SearchObject *)[_tableData objectAtIndex:indexPath.row];
cell.textLabel.text = [[[[so tweet] stringByReplacingOccurrencesOfString:@""" withString:@"\""] stringByReplacingOccurrencesOfString:@"<" withString:@"<"] stringByReplacingOccurrencesOfString:@">" withString:@">"];
cell.detailTextLabel.text = [so fromUser];
if (cell.imageView.image == nil) {
NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:[so userProfileImageURL]]];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:req delegate:self];
[conn start];
}
if ([_cellImages count] > indexPath.row) {
cell.imageView.image = [UIImage imageWithData:[_cellImages objectAtIndex:indexPath.row]];
}
return cell;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[_cellData appendData:data];
[_cellImages addObject:_cellData];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.tableView reloadData];
}
You are appending the data from every image downloaded to the same data object. So in the best case the data object ends up with the data for image #1 immediately followed by the data for image #2 and so on; the image decoder is apparently taking the first image in the chunk of data and ignoring the garbage after. You also seem to be unaware that NSURLConnections’
connection:didReceiveData:will not necessarily be called in the order that the connections were started, thatconnection:didReceiveData:can be called zero or multiple times per connection (and probably will if your images are more than a few kibibytes), and thattableView:cellForRowAtIndexPath:is not guaranteed to be called for every cell in the table in order. All of which are going to totally screw up your_cellImagesarray.To do this right, you need to have a separate NSMutableData instance for each connection, and you need to add it to your
_cellImagesarray just once, and at the correct index for the row rather than at the arbitrary next available index. And then inconnection:didReceiveData:you need to figure out the correct NSMutableData instance to append to; this could be done by using the connection object (wrapped in an NSValue usingvalueWithNonretainedObject:) as the key in an NSMutableDictionary, or usingobjc_setAssociatedObjectto attach the data object to the connection object, or by making yourself a class that handles all the management of the NSURLConnection for you and hands you the data object when complete.