9

I've been running down slow scroll performance and I've noticed that when I scroll and setImage gets called with non-cached images, the performance lags while the download happens.

if let imageURL = URL(string: presentable.imageUrl) {
    let resource = ImageResource(downloadURL: imageURL)
    photoView.kf.setImage(with: resource, options: [.transition(.fade(0.2))])
}

My understanding is that Kingfisher downloads these on a background thread and then displays them on the main thread but the main thread seems to be temporarily blocked. Removing the .transition doesn't help the situation.

Any ideas on how to improve my scroll performance? Thanks

Zack Shapiro
  • 6,648
  • 17
  • 83
  • 151
  • The scrolling stutters while the image loads in and then resumes. I'm just using KF's out of the box implementation for setImage, nothing custom – Zack Shapiro Jan 07 '19 at 22:07
  • And is the image appropriately sized for the image view or is it much larger? I could imagine that could cause some slight stuttering... – Rob Jan 07 '19 at 22:12
  • It's likely larger. I can't control the image sizing, it's user-generated content – Zack Shapiro Jan 07 '19 at 22:13
  • 1
    Yeah, but you could resize in the background. But I’d be surprised that KingFisher’s download is causing the hitch because that’s its whole _raison d’être._ – Rob Jan 07 '19 at 22:17
  • 1
    That was my thinking. Let me try resizing first and then giving it to KF – Zack Shapiro Jan 07 '19 at 22:19
  • KF has `backgroundDecode` option. Try it. – Ryan Jan 07 '19 at 22:20

2 Answers2

19

When the images are larger than the image view, iOS needs to manipulate large UIImage objects, which can cause observable stuttering in the UI. You can prevent that problem by resizing the images before using them.

Fortunately, Kingfisher has a processor that can resize these (in a background thread) for you. As the Cheat Sheet says:

Using DownsamplingImageProcessor for high resolution images

Think about the case we want to show some large images in a table view or a collection view. In the ideal world, we expect to get smaller thumbnails for them, to reduce downloading time and memory use. But in the real world, maybe your server doesn't prepare such a thumbnail version for you. The newly added DownsamplingImageProcessor rescues [sic]. It downsamples the high-resolution images to a certain size before loading to memory:

imageView.kf.setImage(
    with: resource,
    placeholder: placeholderImage,
    options: [
        .processor(DownsamplingImageProcessor(size: imageView.size)),
        .scaleFactor(UIScreen.main.scale),
        .cacheOriginalImage
    ])

Typically, DownsamplingImageProcessor is used with .scaleFactor and .cacheOriginalImage. It provides a reasonable image pixel scale for your UI, and prevent future downloading by caching the original high-resolution image.

I created a little test with small images and confirmed that it was silky smooth, but when I used large images, I experienced stuttering in the scrolling behavior. But when I added this DownsamplingImageProcessor in the large image scenario, it was silky smooth again.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • This was true in my case... we should not load images larger than container. – NSPratik Jun 05 '20 at 06:26
  • 1
    Yep, we shouldn't load images larger than the container times the scale for that device (e.g. a 100×100 pt image view on a 3× device, should use images that are 300×300 px). – Rob Aug 16 '20 at 21:49
  • Also content mode matters sometimes, check my answer... – NSPratik Aug 17 '20 at 06:50
1

Updating

self.imageView.contentMode = .scaleAspectFill

to

self.imageView.contentMode = .scaleAspectFit

did the trick for me.

I have embedded UICollectionView inside UITableViewCell. Each UICollectionViewCell has an image view which loads image from URL using Kingfisher. My table view scrolling was a lot jerky and I tried different things including not to bind model and setting data to each cell. But nothing made any difference whatsoever. Once I changed content mode of image view, it is now scrolling very smoothly.

As per Rob's answer, it is also possible that setting mode to .scaleAspectFill might be required to do some processing even-though the image size is smaller (even 480 x 320 in my case). After updating mode to .scaleAspectFit, now its scrolling smoothly even for large-sized images.

NSPratik
  • 4,714
  • 7
  • 51
  • 81