2

Please see this simple CATiledLayer example https://github.com/seanhess/CATiledLayer-Example

It consists of one viewController with a hierarchy like this:

view: (frame = window size)
    scrollView: (frame = window size, content size = 200 x 4000)
        contentView: (frame = content size = 200 x 4000, tile size = 100 x 100)

The content view's layer has been overriden to be a CATiledLayer.

If you run the linked code, you'll see that tiles with the same rect are requested multiple times. It happens both when you first run the code, and when you scroll.

Switch to branch "one-column" - it only happens on init, never when you scroll down.

Switch to branch "default-tile-size" - it only happens on init, but very rarely (you have to run it multiple times before it happens)

I'm trying to write some code in drawLayer:inContext: that locates the correct data and draws it. It could be expensive, and I don't want to do it more than once.

Any idea what is going on? What could I do differently?

Sean Clark Hess
  • 15,859
  • 12
  • 52
  • 100
  • I suspect that nobody really knows CATiledLayer very well, aside from using it to draw huge images. :( Suggestions on where to go for help? – Sean Clark Hess Apr 06 '11 at 03:35
  • I'm seeing the same thing. I have a small demo app with just 12 tiles that show on the screen without scrolling and the drawing code gets called 22 times. I'll keep looking into this, too. – EricK Apr 22 '11 at 14:13
  • Amongst the other issues with CATiledLayer, I was wondering the same. Maybe you could separately track which tiles have been requested and simply skip expensive operation for the rect that has been already requested? – TheBlack Apr 28 '11 at 07:07
  • @TheBlack - that is what I ended up doing, but I'll check out EricK's stuff, below. – Sean Clark Hess Apr 30 '11 at 23:10

5 Answers5

2

This is a bug in IOS. It happens when the CPU is dual-core, in that case there are two threads each requesting each tile. That means that the bug is present in the simulator, and on the iPhone 4S, but not on the other iPhone models. I assume it will also be present on dual-core iPads.

I have reported the bug to Apple long ago already (for the simulator) and recently again (for the iPhone 4S). Apple recently gave the impression that they have solved it in IOS 6.

fishinear
  • 6,101
  • 3
  • 36
  • 84
  • I see the same behavior here, but hadn't had the chance to test it on iOS 6. Any update on this would be appreciated as soon as iOS 6 has left beta. – Klaas Jul 31 '12 at 17:55
  • Apple has asked me to test the bug on iOS 6 beta. But I am not really in the position to do that at this moment (I have switched to my own implementation of tiled layers). But if somebody tests it on iOS 6 beta, then I can report back to Apple the results. – fishinear Aug 02 '12 at 09:58
  • I reported the issue recently on iOS 6.0.1, and it was flagged a duplicate of another issue. I can't see the other issue on Apple's bugreporter, but I can see that it's status is still "Open" – Airsource Ltd Feb 01 '13 at 15:52
  • I have just retested this on iOS 6.0.1, and it seems to be solved on the device itself (at least on an iPhone 4S with 6.0.1). However it is still not solved on the simulator. And the setNeedsDisplay bug is not solved either: you can still get a parallel redraw of the same tile by two threads, if you call setNeedsDisplay while drawing is in progress (which is hard to avoid...). FYI, my original bug on the simulator is 10456864 (duplicate), for the iPhone 4S: 11768030 (closed). The setNeedsDisplay bug: 10456578 (open) – fishinear Feb 07 '13 at 16:51
  • @fishinear you switched to your own solution? I need that too. Can you help me? You can contact me via email. – Fab1n Feb 23 '13 at 08:54
1

Very well solved here:

https://stackoverflow.com/a/8783396/341994

The problem is that (at least in my testing) drawRect: is called simultaneously on two threads. By logging, I can see individual lines of my code being executed twice before we go on to the next line, which is also executed twice! The logging shows that this is because one thread is executing a line and then another thread is executing the same line - and then they both go on to the next line.

This is as nasty as it gets. The solution is to wrap the the whole interior of drawRect: in a dispatch_sync on a serial queue.

Community
  • 1
  • 1
matt
  • 515,959
  • 87
  • 875
  • 1,141
0

Have you tried your code on a device? I consistently see this behavior in the simulator, but not on my iPad 1. I just tried this again in an app that is displaying a large tiled image. My tile size is 256 and on the device my drawRect: method is being called 12 times, which is how many tiles are on the screen. In the simulator, the method gets called between 20 and 23 times, with all but the last few tiles being drawn twice.

EricK
  • 161
  • 3
  • I have tested on an iPhone 4S, and it is present there. The iPad 1 is single core, which explains why the bug does not show up. I expect the bug to be present on the iPad 2 & 3 as well. – fishinear Aug 02 '12 at 10:58
0

Isn't this due to the setLevelsOfDetailBias ? When I leave everything standard the tiles only get rendered once (testing on the device). But when I set the levelofdetailbias to 2, the tiles get loaded multiple times. I think that's due to the fact that CATiledLayer just starts caching extra detail levels when the device is idle.

polyclick
  • 2,704
  • 4
  • 32
  • 58
0

In the end, the answers didn't work. It is fairly simple, though, to make your delegate method be aware of whether or not it has drawn that cycle.

Sean Clark Hess
  • 15,859
  • 12
  • 52
  • 100
  • Actually, if you have drawing cycles that are close together, this might be more difficult than you expect. Especially because of another bug in CATiledLayer: when you call "setNeedsDisplay" while it is in the process on drawing a tile, it will not redraw that tile. – fishinear Aug 02 '12 at 10:01