Note that there are some explanatory texts on larger screens.

plurals
  1. PONon-lazy image loading in iOS
    text
    copied!<p>I'm trying to load UIImages in a background thread and then display them on the iPad. However, there's a stutter when I set the imageViews' view property to the image. I soon figured out that image loading is lazy on iOS, and found a partial solution in this question:</p> <p><a href="https://stackoverflow.com/questions/1815476/cgimage-uiimage-lazily-loading-on-ui-thread-causes-stutter">CGImage/UIImage lazily loading on UI thread causes stutter</a></p> <p>This actually forces the image to be loaded in the thread, but there's still a stutter when displaying the image.</p> <p>You can find my sample project here: <a href="http://www.jasamer.com/files/SwapTest.zip" rel="nofollow noreferrer">http://www.jasamer.com/files/SwapTest.zip</a> (edit: <a href="http://www.jasamer.com/files/SwapTest-Fixed.zip" rel="nofollow noreferrer">fixed version</a>), check the SwapTestViewController. Try dragging the picture to see the stutter.</p> <p>The test-code I created that stutters is this (the forceLoad method is the one taken from the stack overflow question I posted above):</p> <pre><code>NSArray* imagePaths = [NSArray arrayWithObjects: [[NSBundle mainBundle] pathForResource: @"a.png" ofType: nil], [[NSBundle mainBundle] pathForResource: @"b.png" ofType: nil], nil]; NSOperationQueue* queue = [[NSOperationQueue alloc] init]; [queue addOperationWithBlock: ^(void) { int imageIndex = 0; while (true) { UIImage* image = [[UIImage alloc] initWithContentsOfFile: [imagePaths objectAtIndex: imageIndex]]; imageIndex = (imageIndex+1)%2; [image forceLoad]; //What's missing here? [self performSelectorOnMainThread: @selector(setImage:) withObject: image waitUntilDone: YES]; [image release]; } }]; </code></pre> <p>There are two reasons why I know the stuttering can be avoided: </p> <p>(1) Apple is able to load images without stuttering in the Photos app</p> <p>(2) This code does not cause stutter after placeholder1 and placeholder2 have been displayed once in this modified version of the above code: </p> <pre><code> UIImage* placeholder1 = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource: @"a.png" ofType: nil]]; [placeholder1 forceLoad]; UIImage* placeholder2 = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource: @"b.png" ofType: nil]]; [placeholder2 forceLoad]; NSArray* imagePaths = [NSArray arrayWithObjects: [[NSBundle mainBundle] pathForResource: @"a.png" ofType: nil], [[NSBundle mainBundle] pathForResource: @"b.png" ofType: nil], nil]; NSOperationQueue* queue = [[NSOperationQueue alloc] init]; [queue addOperationWithBlock: ^(void) { int imageIndex = 0; while (true) { //The image is not actually used here - just to prove that the background thread isn't causing the stutter UIImage* image = [[UIImage alloc] initWithContentsOfFile: [imagePaths objectAtIndex: imageIndex]]; imageIndex = (imageIndex+1)%2; [image forceLoad]; if (self.imageView.image==placeholder1) { [self performSelectorOnMainThread: @selector(setImage:) withObject: placeholder2 waitUntilDone: YES]; } else { [self performSelectorOnMainThread: @selector(setImage:) withObject: placeholder1 waitUntilDone: YES]; } [image release]; } }]; </code></pre> <p>However, I can't keep all my images in memory.</p> <p>This implies that forceLoad doesn't do the complete job - there's something else going on before the images are actually displayed. Does anyone know what that is, and how I can put that into the background thread?</p> <p>Thanks, Julian</p> <p><strong>Update</strong></p> <p>Used a few of Tommys tips. What I figured out is that it's CGSConvertBGRA8888toRGBA8888 that's taking so much time, so it seems it's a color conversion that's causing the lag. Here's the (inverted) call stack of that method.</p> <pre><code>Running Symbol Name 6609.0ms CGSConvertBGRA8888toRGBA8888 6609.0ms ripl_Mark 6609.0ms ripl_BltImage 6609.0ms RIPLayerBltImage 6609.0ms ripc_RenderImage 6609.0ms ripc_DrawImage 6609.0ms CGContextDelegateDrawImage 6609.0ms CGContextDrawImage 6609.0ms CA::Render::create_image_by_rendering(CGImage*, CGColorSpace*, bool) 6609.0ms CA::Render::create_image(CGImage*, CGColorSpace*, bool) 6609.0ms CA::Render::copy_image(CGImage*, CGColorSpace*, bool) 6609.0ms CA::Render::prepare_image(CGImage*, CGColorSpace*, bool) 6609.0ms CALayerPrepareCommit_(CALayer*, CA::Transaction*) 6609.0ms CALayerPrepareCommit_(CALayer*, CA::Transaction*) 6609.0ms CALayerPrepareCommit_(CALayer*, CA::Transaction*) 6609.0ms CALayerPrepareCommit_(CALayer*, CA::Transaction*) 6609.0ms CALayerPrepareCommit 6609.0ms CA::Context::commit_transaction(CA::Transaction*) 6609.0ms CA::Transaction::commit() 6609.0ms CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) 6609.0ms __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ 6609.0ms __CFRunLoopDoObservers 6609.0ms __CFRunLoopRun 6609.0ms CFRunLoopRunSpecific 6609.0ms CFRunLoopRunInMode 6609.0ms GSEventRunModal 6609.0ms GSEventRun 6609.0ms -[UIApplication _run] 6609.0ms UIApplicationMain 6609.0ms main </code></pre> <p>The last bit-mask changes he proposed didn't change anything, sadly.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload