Scrolling Performance and UIImage Drawing

I create a UITableView similar to the iPod.app album view:

IMG_2316.PNG

I import all artists and album art from the iPod library on first launch. Saving everything in CoreData and returning it to NSFetchedResultsController. I am reusing cell IDs, and in my cellForRowAtIndexPath: method cellForRowAtIndexPath: I have this code:

 Artist *artist = [fetchedResultsController objectAtIndexPath:indexPath]; NSString *identifier = @"bigCell"; SWArtistViewCell *cell = (SWArtistViewCell*)[tableView dequeueReusableCellWithIdentifier:identifier]; if (cell == nil) cell = [[[SWArtistViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease]; cell.artistName = artist.artist_name; cell.artworkImage = [UIImage imageWithData:artist.image]; [cell setNeedsDisplay]; return cell; 

My SWArtistViewCell cell implements the drawRect: method for drawing both a row and an image:

 [artworkImage drawInRect:CGRectMake(0,1,44,44)] [artistName drawAtPoint:CGPointMake(54, 13) forWidth:200 withFont:[UIFont boldSystemFontOfSize:20] lineBreakMode:UILineBreakModeClip]; 

Scrolling is still volatile, and I just can't figure out why. Applications like iPods and Twitter have oily smooth scrolling, and yet they both draw a small image in the camera, just like me.

All my views are opaque. What am I missing?

EDIT : here's what the Shark says:

alt text

I am not familiar with the Shark. Any pointer on what these characters are associated with? When I look at their trail, they all point to my drawRect: method, specifically the UIImage drawing.

alt text

Would this point to something else if chokehold is reading a file? Is this definitely a drawing?

EDIT: save image

I did as pothibo suggested, and added the artworkImage method to my Artist class, which saves the image created using imageWithData:

 - (UIImage*)artworkImage { if(artworkImage == nil) artworkImage = [[UIImage imageWithData:self.image] retain]; return artworkImage; } 

So, now I can directly set the saved image to my TableViewCell as follows:

 cell.artworkImage = artist.artworkImage; 

I also set my setNeedsDisplay inside the setArtworkImage: method of my tableViewCell class. Scrolling is still lagging, and Shark is showing exactly the same results.

+4
source share
5 answers

At the moment, it's best to use tools to try and find bottlenecks in your code.

0
source

Profiling data strongly indicates that a bottleneck is in the unpacking of your PNG images. My guess is that 58.5% of your expected CPU time is spent unpacking PNG data (i.e. if the memcpy call is also included in the download). Probably even more time is spent there, but it's hard to say without extra data. My suggestions:

  • As stated above, save uploaded images in UIImage and not in NSData. This way, you donโ€™t have to decompress PNG every time the image is displayed.
  • Put the loading of images in the workflow so as not to affect the responsiveness of the main thread (the same amount). Creating a worker is very simple:

    [self performSelectorOnMainThread: @selector (preloadThreadEntry :) withObject: nil waitUntilDone: NO];

  • Preload the images forward, for example, 100 lines or more (for example, 70 in the scroll direction, keep 30 in the opposite direction). If all your images should be 88x88 pixels on the retina, 100 images will require no more than two MB.

  • When you comment more on calls to materials with the names "png", "gz", "inflate", etc., it may not go down your list, but they certainly will not affect the feel of the application in such a bad way.

Only if you still have performance problems after this, I would recommend that you study the scaling and, for example, load images "... @ 2x.png" for the retina. Good luck

+6
source

[UIImage imageWithData:] does not cache.

This means that CoreGraphic decompresses and processes your image every time you pass this dataSource method.

I would modify the artist object to hold UIImage instead of NSData. You can always clear the image in memory if you get a lot of them.

Also, I would not recommend using setNeedsDisplay inside a dataSource call, I would use it from your cell.

SetNeedsDisplay is not a direct call to drawRect:

It only tells os to draw UIVIew again at the end of runloop. You can call setNeedsDisplay 100 times in the same runloop, and the OS will only call the drawRect method once.

+4
source

I guess the delay is storing images in Core Data. Master data is usually not a good way to store large blocks of data.

A better solution would be to save the images as separate files on disk using an album identifier to identify each image. Then you must configure the memory cache to store images in RAM for quick loading in UIImageView s. Loading images from disk to RAM should ideally be done in the background thread (for example, try performing SelectorOnBackgroundThread) so that the inputs / outputs do not fail in the main thread (which will affect your scroll performance).

Appendix 1

If you only -drawRect: once per load of each cell (as it should be), the problem may be the scaling of the images. Scaling an image by drawing it in code using drawInRect will use processor time, so an alternative approach is to scale the images as they arrive from the iPod library (if you need them in different sizes, save the version in each size, required). You may need to do this in the background thread when importing data to avoid blocking the main (UI) stream.

You might think that UIImageView can scale it using Core Animation, which means it's hardware accelerated (I'm not sure if this is true, I just guess). Thus, the transition to UIImageView for the image will be relieved of the processor load on image scaling. You will have a slight increase in composition overhead, but this may be the easiest way to get closer to the โ€œoptimalโ€ scroll performance.

+1
source

If the delay occurs in -drawRect , then you can take a look at this article : the Tweetie developer explains in some detail the method that he used to get this smooth scroll. Since then, it has become a little easier: CALayer has a property shouldRasterize , which basically aligns its sublayers into a bitmap, which can then - as long as nothing changes inside the layer - give you much better performance when animating the layer around, like a UITableView when you scroll his. In this case, you will probably apply this property to your individual UITableViewCell s layers.

+1
source

All Articles