36

I have a gallery in my app utilizing a UICollectionView. The cells are approximately 70,70 size. I am using ALAssets from the ALAssetLibrary in the gallery which I have stored in a list.

I am using the usual pattern for populating the cells:

-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{

  mycell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
  mycell.imageView.image = [[UIImage imageWithCGImage:[alassetList objectAtIndex:indexpath.row] thumbnail]];
  return mycell;
}

My gallery is scrolling choppy. I don't understand why this is. I've tried adding a NSCache to cache the thumbnail images (thinking maybe creating the images was expensive) but this did not help for the performance.

I would expect the UI to be as buttery as the stock app.

I am now suspecting it may be something in the UICollectionViewCell prepareForReuse that may be holding up the dequeueReusableCellWithReuseIdentifier method but using instruments I was not able to find this.

Any other thing that may be be causing this? Is there a "faster" way to prepare the UICollectionViewCell or to dequeue them in a faster fashion?

Meet Doshi
  • 4,241
  • 10
  • 40
  • 81
Avba
  • 14,822
  • 20
  • 92
  • 192
  • 1
    Using instruments, did you spot a method call which took too much time? E.g. 0.02 sec for reusing one cell is too much for smooth scrolling. – Daniel S. Aug 27 '13 at 08:39
  • Yes. After doing that I saw the method [uicollectionview updatevisiblecellsnow] was being called tons of times. Googled that problem and got to this question http://stackoverflow.com/questions/16336772/uicollectionview-performance-updatevisiblecellsnow. – Avba Aug 27 '13 at 08:44
  • 1
    Try moving some of the cell's display-related functions to the collection view's willDisplayCell method and others to the didEndDisplayingCell method. – James Bush Jul 08 '16 at 17:29
  • 1
    Here's my latest iteration of a perfectly smooth-scrolling collection view with real-time video previews (up to 16 at a time: https://youtu.be/7QlaO7WxjGg It even uses a cover flow custom layout and "reflection" view that mirrors the video preview perfectly. The source code is here: http://www.mediafire.com/download/ivecygnlhqxwynr/VideoWallCollectionView.zip – James Bush Aug 06 '16 at 02:52

6 Answers6

97

So anybody having scrolling issues should do this

add these 2 lines after your dequeue

cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
Community
  • 1
  • 1
Avba
  • 14,822
  • 20
  • 92
  • 192
  • 4
    Sorry, what should I do? Your link points to a page with lots and lots of content. Should I read all that if I have ++any++ scrolling issue? Or just if it's laggy? – Daniel S. Aug 27 '13 at 08:50
  • I'm not sure what your issue is exactly. You can point me to your question, but in my case it turns out that the UICollectionView was causing the laggyness and my code was actually performing well. In my case forcing the cell layer to raster solved the issue. In other cases you can try to use the Time Instument in XCode to understand the slowness. If it is your code or some system code. – Avba Aug 27 '13 at 09:27
  • in short just try adding the 2 lines I posted to your cellforitematindexpath method after dequing your cell and see if it helps you out. – Avba Aug 27 '13 at 09:28
  • @AvnerBarr This doesn't seem to work. Should we put this in `- (Cell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath` – rahulg Mar 26 '14 at 04:52
  • I haven't tried on the 6plus - but I would assume that loading cells is much faster there. If you are loading images from ALAssets I would highly suggest using this: https://developer.apple.com/library/ios/documentation/Photos/Reference/PHImageManager_Class/index.html – Avba Sep 28 '14 at 05:51
  • 4
    @AvnerBarr thanks for the fantastic answer, and your answers on the other question too http://stackoverflow.com/questions/16336772/ I've added a small bounty to say Thanks from everyone :) cheers – Fattie Oct 01 '14 at 04:21
  • I am using custom `UICollectionViewCell` subclasses. Should I still set these properties each time I dequeue for reuse, or is it enough to call them just once on `initWithCoder:`? – Nicolas Miari Nov 09 '15 at 07:11
  • probably once as the cell is reused. I don't think that the property would be changed. – Avba Nov 09 '15 at 10:26
  • 1
    But I wouldn't set the property in the cell itself as rasterizing has memory/performance implications that you may not necessarily want. But as others have mentioned here in various forms , the main performance hit stems from system decompressing UIImage [UIImage image...] on the main thread. This is costly on large images (check the new iOS-8 api for PHImageManager which can help). In the worst case I would suggest force decompressing the image on a background thread before assigning it to a UIImageView – Avba Nov 09 '15 at 10:31
  • 1
    From my observations, this causes more lag (using PHCachingManager) on the cell, the best solution was to set the rasterize on the image view after setting the image (this is important), the lag is nearly gone (using also the startCaching from the apple sample). – Pion Mar 22 '16 at 09:48
  • Great answer! Helped my problem – Liam Bolling Oct 27 '19 at 23:43
19

I would assume the "choppyness" is coming from the UIImage allocation, not anything with the dequeueReusableCellWithReuseIdentifier method. I'd try doing the image allocation on a background thread and see if that makes things a little more buttery.

-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
  mycell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
     // Load image on a non-ui-blocking thread
    UIImage *image = [[UIImage imageWithCGImage:[alassetList objectAtIndex:indexpath.row] thumbnail]];

    dispatch_sync(dispatch_get_main_queue(), ^(void) {
        // Assign image back on the main thread
        mycell.imageView.image = image;
    });
  });

  return mycell;
}

More details can be found here: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/Introduction/Introduction.html

preynolds
  • 780
  • 8
  • 13
8

Load your images using NSURLConnection's sendAsynchronousRequest:queue:completionHandler:

NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
    [cell.imageView setImage:[UIImage imageWithData:data]];
}];
Beau Hankins
  • 103
  • 1
  • 8
1

Tick on clip subviews and opaque in attributes inspector and tick off paging enabled.

Alvin George
  • 14,148
  • 92
  • 64
0

I had issues with UICollectionView scrolling.

What worked (almost) like a charm for me: I populated the cells with png thumbnails 90x90. I say almost because the first complete scroll is not so smooth, but never crashed anymore...

In my case, the cell size is 90x90.

I had many original png sizes before, and it was very choppy when png original size was greater than ~1000x1000 (many crashes on first scroll).

So I select 90x90 (or the like) on the UICollectionView and display the original pngs (no matter the size). Hope this helps others.

shim
  • 9,289
  • 12
  • 69
  • 108
0

If anyone is using PhImageManager, turning off synchronous solved the problem. (deliveryMode = .FastFormat can also give additional performance enhancement, but the tradeoff is the thumbnail will be of lower quality)

    let option = PHImageRequestOptions()
    option.deliveryMode = .Opportunistic
    option.synchronous = false
    PHImageManager().requestImageForAsset(phAsset, targetSize: CGSizeMake(2048, 2048), contentMode: .AspectFit, options: option, resultHandler: { (image, objects) in
        self.imageView.image = image!
    })
NoWhereToBeSeen
  • 1,404
  • 1
  • 13
  • 22