Prevent changes to UITableViewCell UIImageView asynchronously loaded UIImage

I am loading the UIImageView asynchronously into my UITableViewCell . The representations of my image are squares of 100 dots by 100 points, but the UIImage that I retrieve do not always have the same width and height.

My problem is this: when loading UIImageView cells UIImageView everything is 100 by 100, which is good, but the internal UIImage stretched to this size (left image). When I click on a cell and it is highlighted, the images ADD TO REDUCE the right aspect ratio (right image). Also, when I scroll down in a UITableView , all the images are also 100x100 and stretch, but when I scroll back again to see the cells again, they suddenly change. I do not know what scale factor they follow.

enter image description here

What am I doing wrong? I already set UIImageView s' contentMode in UIViewContentModeCenter , but as you can see, it does not work. Please help? My complete code is below.

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"searchResultCell"]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"searchResultCell"]; } // Get the Ad object that will be displayed in this cell. Ad *ad = (Ad *)[self.searchResults objectAtIndex:indexPath.row]; // Set up the detail text on the right. cell.textLabel.attributedText = ad.attributedTextDisplay; cell.textLabel.numberOfLines = 0; cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping; // Set up the image on the left. NSString *thumbnailURL = ad.thumbnailURL; NSData *data = [FTWCache objectForKey:[MD5 hash:thumbnailURL]]; // If the image has been previously downloaded and is stored in FTWCache. if (data) { // Then use that data instead. cell.imageView.image = [UIImage imageWithData:data]; } else { // Assign default image before beginning background image retrieval task. cell.imageView.image = [UIImage imageNamed:@"image_stub_ad"]; // Trigger background task that retrieves the actual thumbnail from the URL. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:thumbnailURL]]; // Proceed only with updating the table view cell if the returned NSData is not nil. if (imageData) { dispatch_sync(dispatch_get_main_queue(), ^{ // UPDATED: Added call to setNeedsDisplay. UITableViewCell *theSameCell = [tableView cellForRowAtIndexPath:indexPath]; theSameCell.imageView.image = [UIImage imageWithData:imageData]; [theSameCell setNeedsDisplay]; }); } }); } // UPDATED: Set the contentMode to UIViewContentModeScaleAspectFit instead of UIViewContentModeCenter. cell.imageView.contentMode = UIViewContentModeScaleAspectFit; return cell; } 
+4
source share
3 answers

Try it.

 // Proceed only with updating the table view cell if the returned NSData is not nil. if (imageData) { dispatch_sync(dispatch_get_main_queue(), ^{ UITableViewCell *imageCell = [tableView cellForRowAtIndexPath:indexPath] imageCell.imageView.image = [UIImage imageWithData:imageData]; [imageCell setNeedsDisplay];//this is the line I added only [FTWCache setObject:imageData forKey:[MD5 hash:thumbnailURL]]; }); } 
0
source
 -(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { ImageViewCell *cell = (ImageViewCell*)[tableView dequeueReusableCellWithIdentifier:@"identifier"]; if (cell==nil) { cell = [[UImageViewCell alloc] init]; //or from whatever way you generate this cell. //Manually set this below on each time the cell is newly created. cell.imageView.contentMode = UIViewContentModeScaleAspectFit; } //Do the update of your imageView image here //( this is the update area when your cell is not nil // and could be a reused cell.) cell.imageView.image = your_ui_image ; [cell.imageView setNeedsDisplay]; return cell; } 
0
source

Not the complete answer, but the “improvement” of starting remote fetching in an asynchronous task:

  // Trigger background task that retrieves the actual thumbnail from the URL. [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse* response, NSData* imageData, NSError* error) { if (error) { [self handleError:error]; // connection failed return; } // here, at a minimum, check status code and Content-Type: // ... add code if (statusCode == 200 && contentTypeOK) // "OK" { [FTWCache setObject:imageData forKey:[MD5 hash:thumbnailURL]]; [tableView cellForRowAtIndexPath:indexPath].imageView.image = [UIImage imageWithData:imageData]; } else { // we didn't get an image, possibly other errors (eg authentication, etc.) NSError* err = ...; // add code [self handleError:err]; return; } }]; 

This code does not have the ability to cancel it, if necessary. There is also no means to prevent multiple requests from starting for the same image.

The aforementioned problems, together with a specific user scenario, can lead to an unlimited number of running network requests, which will ultimately lead to an application crash due to memory problems. To improve this, we need to use the NSURLConnection implementation of the asynchronous style with delegates, which gives us the opportunity to cancel the request and perform additional measurements that will not be caused by multiple requests.

Edit:

An additional improvement will be the insertion of image data into the cache, as well as the creation of a UIImage object in the background job, the completion handler of which updates the cell and executes it in the main thread.

0
source

Source: https://habr.com/ru/post/1494678/


All Articles