UITableView with images scrolls very slowly

Possible duplicate:
View image table, slow loading and scrolling

I have a UITableView that loads images for UITableViewCells from a server. I noticed that the tableView scrolls very slowly.

I thought this might load the problem, but I realized that the table still scrolls slowly after the download is complete, and the image icon size is smaller.

I searched Google but did not find any help.

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { btnBack.hidden = FALSE; static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.backgroundColor = [UIColor clearColor]; cell.textLabel.font = [UIFont fontWithName:@"Noteworthy" size:17.0]; cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0]; cell.textLabel.textColor = [UIColor blackColor]; cell.textLabel.highlightedTextColor = [UIColor blackColor]; } cell.textLabel.text = [NSString stringWithFormat:@" %@", [test.arrTitle objectAtIndex:indexPath.row]]; NSString *Path; Path = [NSString stringWithFormat:@"http://%@",[test.arrImages objectAtIndex:indexPath.row]]; NSLog(@"image-->%@",[test.arrImages objectAtIndex:indexPath.row]); NSString *strImage = Path; NSURL *url4Image = [NSURL URLWithString:strImage]; NSData *data = [NSData dataWithContentsOfURL:url4Image]; image =[[UIImage alloc] initWithData:data]; cell.imageView.image =image; [image release]; return cell; } 
+4
source share
6 answers

While my initial answer, below, tried to solve several key issues related to asynchronous image retrieval, it is still mostly limited. A correct implementation also ensures that if you scroll quickly, the visible cells take precedence over the cells that scroll from the screen. It will also support canceling previous requests (and canceling them for you when necessary).

Although we could add these features to the code below, it's probably best to take an installed, tested solution that uses the NSOperationQueue and NSCache technologies discussed below, but also fixes the above issues. The easiest solution is to accept one of the installed UIImageView categories, which supports asynchronous image retrieval. The AFNetworking and SDWebImage libraries have UIImageView that gracefully handle all of these problems.


You can use NSOperationQueue or GCD for your lazy loading (see Concurrency Programming Guide for a discussion of various asynchronous operation technologies). The first advantage is that you can specify exactly how many simultaneous operations are permissible, which is very important when downloading images from the Internet, because many web servers limit the number of concurrent requests that they will receive from this client.

Main idea:

  • Send a request for image data in a separate background queue;
  • When the image download is complete, send the user interface update back to the main queue because you should never update user interfaces in the background;
  • When you start sending the final user interface update code in the main queue, make sure that the UITableViewCell is still displayed and that it has not been deleted and reused, because the corresponding cell scrolls from the screen. If you do not, an incorrect image may occur.

You would like to replace your code with something like the following code:

First, define a property for NSOperationQueue that you will use to load images, and also NSCache to save these images:

 @property (nonatomic, strong) NSOperationQueue *imageDownloadingQueue; @property (nonatomic, strong) NSCache *imageCache; 

Second, initialize this queue and cache in viewDidLoad :

 - (void)viewDidLoad { [super viewDidLoad]; self.imageDownloadingQueue = [[NSOperationQueue alloc] init]; self.imageDownloadingQueue.maxConcurrentOperationCount = 4; // many servers limit how many concurrent requests they'll accept from a device, so make sure to set this accordingly self.imageCache = [[NSCache alloc] init]; // the rest of your viewDidLoad } 

Thirdly, your cellForRowAtIndexPath might look like this:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { btnBack.hidden = FALSE; static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.backgroundColor = [UIColor clearColor]; cell.textLabel.font = [UIFont fontWithName:@"Noteworthy" size:17.0]; cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0]; cell.textLabel.textColor = [UIColor blackColor]; cell.textLabel.highlightedTextColor = [UIColor blackColor]; } cell.textLabel.text = [NSString stringWithFormat:@" %@", [test.arrTitle objectAtIndex:indexPath.row]]; // code change starts here ... initialize image and then do image loading in background NSString *imageUrlString = [NSString stringWithFormat:@"http://%@", [test.arrImages objectAtIndex:indexPath.row]]; UIImage *cachedImage = [self.imageCache objectForKey:imageUrlString]; if (cachedImage) { cell.imageView.image = cachedImage; } else { // you'll want to initialize the image with some blank image as a placeholder cell.imageView.image = [UIImage imageNamed:@"blankthumbnail.png"]; // now download in the image in the background [self.imageDownloadingQueue addOperationWithBlock:^{ NSURL *imageUrl = [NSURL URLWithString:imageUrlString]; NSData *imageData = [NSData dataWithContentsOfURL:imageUrl]; UIImage *image = nil; if (imageData) image = [UIImage imageWithData:imageData]; if (image) { // add the image to your cache [self.imageCache setObject:image forKey:imageUrlString]; // finally, update the user interface in the main queue [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // Make sure the cell is still visible // Note, by using the same `indexPath`, this makes a fundamental // assumption that you did not insert any rows in the intervening // time. If this is not a valid assumption, make sure you go back // to your model to identify the correct `indexPath`/`updateCell` UITableViewCell *updateCell = [tableView cellForRowAtIndexPath:indexPath]; if (updateCell) updateCell.imageView.image = image; }]; } }]; } return cell; } 

Fourthly, and finally, although it would be possible to write code to clear the cache in situations with low memory, it turns out that it does this automatically, so no additional processing is required here. If you manually simulate the low memory situation in the simulator, you will not see that it crowds out its objects, because NSCache does not respond to UIApplicationDidReceiveMemoryWarningNotification , but during the actual operation, when the memory is low, the cache will be cleared. In fact, NSCache no longer elegantly responds to low memory situations, so you really need to add an observer for this notification and empty the cache in low memory situations.

I could offer many other optimizations (for example, perhaps also caching images in persistent storage to optimize future operations, I actually put all this logic in my own AsyncImage class), but first let's see if this solves the main performance problem.

+18
source

Write this in your UITableView cellForRowAtIndex: Method

 asyncImageView = [[AsyncImageView alloc]initWithFrame:CGRectMake(30,32,100, 100)]; [asyncImageView loadImageFromURL:[NSURL URLWithString:your url]]; [cell addSubview:asyncImageView]; [asyncImageView release]; 

You must import the AsyncImageView class and create an object for this class.

+1
source

Scrolling is very slow because you are loading images into the main stream, i.e. synchronously. You can do the same in the background thread ie asynchronously, see SDWebImage .

0
source

It is advisable that you save the images in an array and fill them in viewDidLoad , and then in cellForRowAtIndexPath: just set

 cell.imageView.image = [yourImageArray objectAtIndex:indexPath.row]; 

As for sluggishness, this is due to the fact that you block the main thread when loading URLDATA into your cellForRowAtIndexPath method, therefore, scrolling until and until the image is extracted, the main thread in which the application is running will be blocked.

0
source

As said before: don't do the hard work in cellForRowAtIndexPath. You can easily get around using GCD. Loading Images from a Background Stream Using Blocks

0
source

You should use NSOperationQueue to handle lazy loading of images and the user table tableviewcell. You can get an example for NSOperationQueue here

Google for tweetie custom tableviewcell This should put you in the right direction.

Apple has a sample project for loading images into tableViews: LazyTableImages

0
source

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


All Articles