Swift 3:Url Image Makes Uitableview Scroll Slow Issue

Swift 3 : URL Image makes UITableView scroll slow issue

I think, that problem here, that you need to cache your images in table view to have smooth scrolling. Every time your program calls cellForRowAt indexPath it downloads images again. It takes time.

For caching images you can use libraries like SDWebImage, Kingfisher etc.

Example of Kingfisher usage:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "identifier", for: indexPath) as! CustomCell

cell.yourImageView.kf.setImage(with: URL)
// next time, when you will use image with this URL, it will be taken from cache.

//... other code
}

Hope it helps

Swift - Slow UITableView scroll (loading image from device storage)

Chances are that postImage is much larger than than the bounds of cell.postImageView, so the assignment on the main thread takes a noticeable amount of time to scale it down. Assuming that postImageView's bounds are foreseeable, scaling the image to the correct size in the background thread will be quite likely to sort this out. If you don't have a resizing function handy, this gist looks reasonable.

UITableView with images scrolls very slowly

While my original answer, below, attempted to solve several key problems associated with asynchronous image retrieval, it is still fundamentally limited. A proper implementation would also make sure that if you scrolled quickly, that the visible cells were prioritized over cells that had scrolled off screen. It would also support cancelation of prior requests (and canceled them for you when appropriate).

While we could add these sorts of capabilities to the below code, it is probably better to adopt an established, proven solution that utilizes the NSOperationQueue and NSCache technologies discussed below, but also addresses the above issues. The easiest solution is to adopt one of the established UIImageView categories that supports asynchronous image retrieval. The AFNetworking and SDWebImage libraries both have UIImageView categories that gracefully handle all of these issues.


You can use NSOperationQueue or GCD to do your lazy loading (see Concurrency Programming Guide for discussion of different asynchronous operations technologies). The former enjoys an advantage that you can specify precisely how many concurrent operations are permissible, which is very important in loading images from the web because many web servers limit how many concurrent requests they will accept from a given client.

The basic idea is:

  1. Submit request of the image data in a separate background queue;
  2. When done downloading image, dispatch UI update back to main queue because you should never do UI updates in the background;
  3. When running dispatched final UI update code on main queue, make sure the UITableViewCell is still visible and that it hasn't been dequeued and reused because the cell in question scrolled off the screen. If you don't do that, the wrong image may momentarily show up.

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

First, define a property for your NSOperationQueue that you will use for downloading images, as well as a NSCache for storing those 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
}

Third, your cellForRowAtIndexPath might look like:

- (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;
}

Fourth, and finally, while one might be inclined to write code to purge the cache in low memory situations, it turns out that it does this automatically, so no extra handling is needed here. If you manually simulate a low memory situation in the simulator, you won't see it evict its objects because NSCache doesn't respond UIApplicationDidReceiveMemoryWarningNotification, but during actual operation, when memory is low, the cache will be purged. Actually, NSCache no longer gracefully responds to low memory situations itself, so you really should add observer for this notification and empty the cache in low memory situations.

I might suggest a bunch of other optimizations (e.g. perhaps also caching images into persistent storage to streamline future operations; I actually put all of this logic in my own AsyncImage class), but first see if this solves the basic performance issue.

How to improve scroll performance when downloading image thumbnail in tableview

  1. For your issue where the previous image is showing when the cell is reused, you should override func prepareForReuse() in PhotoTableViewCell and set photoImageView.image = nil.
  2. For your performance issue, it looks like you're making the API request off the main thread, so that's a good start. How large are the images you're requesting? I'm wondering if the data is so large that it's taking a lot of time to convert it to an image and then set the image on the image view

UITableView scrolls slow after downloading cell images

The main problem is that [NSData dataWithContentsOfURL:] method is a synchronous request. It blocks the thread where is running until the request is done.

You should avoid this type of interaction if you run this on the main thread. This thread will be frozen and the user will be disappointed ;)

You have many solutions for this. A simple one is to use third library like SDWebImage or AFNetworking.

Both have categories around UIImageView class that allows to use them in a simple manner. For example, using UIImageView+AFNetworking you should do like the following:

[cell.imageView setImageWithURL:yourURL placeholderImage:yourPlaceholderImage];

Slow loading of images in UITableViewController extracted from URL string

Try this

    var image: UIImage
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {() -> Void in
// Background thread stuff.
let url = NSURL(string: blogPost.postImageUrl)
let data = NSData(contentsOfURL: url!)
image = UIImage(data:data)

dispatch_async(dispatch_get_main_queue(), {() -> Void in
// Main thread stuff.
cell.imageView.image = image
})
})

Slow scroll on UITableView images

You can read the answer I have just submitted here:

Loading image from CoreData at cellForRowAtIndexPath slows down scrolling

The basic idea is to use Grand Central Despatch to move your table-view-image-getting code to a separate thread, filling in your cells back on the main thread as the images become available. Your scrolling will be super-smooth even if there's a delay loading the images into memory from the filesystem.

UITableview scroll freezes and becomes slow with usage

Okay it was from the gesture recognizer as i said. I just replaced it with a button with the same size of the pic. It's on the pic with alpha = 0. Whenever i need the tap recognition, i just activate the alpha of the button which already has an IBAction. Hope this helps...

iOS tableview lags on scrolling when loading image to imageview

Remove following line from your source code, as it downloads image data during activation (every time when your cell is being reused) of your tableview cell. Also note, it may loads data foreground and that may be the reason for lag when scrolling tableview.

NSData *data = [NSData dataWithContentsOfURL:url];

Following code handles image representation (downloading of image in background and make it visible in foreground) for your tableview cell. So above code is not required in your cell.

[cell.image sd_setImageWithURL:[NSURL URLWithString:imageUrl] placeholderImage:[UIImage imageNamed:@"image.png"]];


Related Topics



Leave a reply



Submit