12

On the iPhone (though I imagine it's an equally valid question in Cocoa) I have a UIScrollView around a UIView backed by a CATiledLayer. The way it works by default is to load any uncached/unfetched tiles when my viewport scrolls over a blank section of the CATiledLayer.

What I would like to know is if there's a way to trigger CATiledLayer to load a tile that's not actively being displayed? I would like to, for example, preload all tiles contiguous to the currently displayed tile while they are still offscreen, thus avoiding flashing a blank screen that fades in to the image once it's loaded asynchronously.

Any ideas?

pebble
  • 3
  • 2
jemmons
  • 18,605
  • 8
  • 55
  • 84
  • Did you figure this out? I would really like to know how you did it if you did. – Drew McCormack Apr 27 '10 at 17:16
  • Nope. Never did. :( Using setNeedsDisplayInRect: works to refresh the content of an already loaded tile, but doesn't seem to cause it to preload. – jemmons Apr 28 '10 at 21:59

3 Answers3

14

I don't think CATiledLayer will do what you want. There are a couple of other options though. First you can disable the tile fade-in and have it display immediately with something like this:

@interface NoFadeTiledLayer : CATiledLayer {
}
@end

@implementation NoFadeTiledLayer
+ (CFTimeInterval)fadeDuration {
    return 0.0;
}
@end

@implementation MyViewWithTiledLayer
+ (Class)layerClass {
    return [NoFadeTiledLayer class];
}
...
@end

Second, you can do your own pre-fetch and caching of the adjacent tiles so they're ready to go when CATileLayer calls drawLayer:inContext. I'd implement scrollViewDidScroll: and scrollViewDidZoom: to determine the adjacent tiles and levelOfDetail. Then do a cache lookup and add any not present to a pre-fetch/render queue. A background thread could service the queue and subsequent scrolls or zooms would clear and rebuild the queue. Then have drawLayer:inContext check the cache first and only fetch/render if necessary.

John Lemberger
  • 2,689
  • 26
  • 25
  • 1
    I've tried this approach to precaching (including the no-fade-duration layer subclass) and it doesn't appear to help much, unfortunately. I'm at a loss as to why; it certainly sounds like a good approach. – Seamus Campbell Mar 17 '11 at 20:28
4

CATileLayer is one of those frustrating classes where it does one thing great, but has no flexibility to it.

At this point all that's left to us is creativity:

1) Make your scroll view huge. I tried 5x the size of the screen before I stopped seeing "blank" tiles. Be wary of memory use! You are drawing to a huge area even though the user only sees 2% of it.

2) Have two version of your image, one high res and one low res. you should be able to blit the low res very quickly and basically you get "blurry" instead of "blank" tiles. Apple's sample code ZoomingPDFViewer shows you how to do this.

http://developer.apple.com/library/ios/#samplecode/ZoomingPDFViewer/Introduction/Intro.html

Of course, some combination of the two might work if you want to invest the time.

amattn
  • 10,045
  • 1
  • 36
  • 33
0

You should try calling setNeedsDisplayInRect: on the areas you wish to display. If you want to keep within tile boundaries you can use the tileSize property to compute tile boundaries.

But I do not know for sure if this will work and we do not know how the tile caching mechanism works.

Jon Steinmetz
  • 4,104
  • 1
  • 23
  • 21