SetNeedsDisplayInRect called during drawLayer: inContext:

I use CATiledLayer in my application, and as a result, this layer is drawn in the background thread. That is, the drawLayer: inContext: method of my delegate is called from the background thread. SetNeedsDisplayInRect, used to cancel invalid parts of CATiledLayer, is always called from the main thread.

Since they are independent streams, sometimes it happens that setNeedsDisplayInRect is called and the background stream is called in the drawLayer: inContext method. I noticed that setNeedsDisplayInRect is ignored in this situation (drawLayer: inContext is not called again).

I registered a bug for Apple because I think this is not true. But it’s hard for me to figure out how to get around this situation. Do you have any good ideas?

EDIT:

I tried out Stanislav's answer using the following code:

- (void) setNeedsDisplayInRect:(CGRect)rect { NSLog(@"setNeedsDisplayInRect:%@", NSStringFromCGRect(rect)); [super setNeedsDisplayInRect:rect]; } - (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)gc { CGRect bounds = CGContextGetClipBoundingBox(gc); NSLog(@"drawLayer:inContext: bounds=%@", NSStringFromCGRect(bounds)); dispatch_async(dispatch_get_current_queue(), ^{ [self setNeedsDisplayInRect:bounds]; }); CGContextSetFillColorWithColor(gc, testColor.CGColor); CGContextFillRect(gc, bounds); sleep(0.2); // simulate the time it takes to draw complicated graphics NSLog(@"end drawLayer:inContext: bounds=%@", NSStringFromCGRect(bounds)); } 

As indicated, the code does cause the drawing indefinitely, but sometimes between setting NeedsDisplayInRect and a delay of up to 5 seconds and the corresponding drawLayer: inContext: in which nothing else happens. See the Journal below for an example. Pay attention to irregular behavior: in the first second some fragments are redrawn several times, others only once. Then there is a pause of 5 seconds, and the cycle starts again.

This was tested on a simulator with IOS6.0 (I choose this version because earlier versions have another error, which is fixed in version 6.0: sometimes they draw the same fragments).

 2012-10-27 15:51:38.771 TiledLayerTest[39934:15a13] drawLayer:inContext: bounds={{0, 300}, {300, 180}} 2012-10-27 15:51:38.774 TiledLayerTest[39934:15a13] end drawLayer:inContext: bounds={{0, 300}, {300, 180}} 2012-10-27 15:51:38.774 TiledLayerTest[39934:1570f] drawLayer:inContext: bounds={{300, 0}, {20, 300}} 2012-10-27 15:51:38.776 TiledLayerTest[39934:1570f] end drawLayer:inContext: bounds={{300, 0}, {20, 300}} 2012-10-27 15:51:38.776 TiledLayerTest[39934:1630b] setNeedsDisplayInRect:{{0, 300}, {300, 180}} 2012-10-27 15:51:38.777 TiledLayerTest[39934:1540f] setNeedsDisplayInRect:{{300, 0}, {20, 300}} 2012-10-27 15:51:38.780 TiledLayerTest[39934:15a13] drawLayer:inContext: bounds={{300, 0}, {20, 300}} 2012-10-27 15:51:38.781 TiledLayerTest[39934:15a13] end drawLayer:inContext: bounds={{300, 0}, {20, 300}} 2012-10-27 15:51:38.782 TiledLayerTest[39934:1540f] setNeedsDisplayInRect:{{300, 0}, {20, 300}} 2012-10-27 15:51:38.789 TiledLayerTest[39934:1570f] drawLayer:inContext: bounds={{0, 0}, {300, 300}} 2012-10-27 15:51:38.791 TiledLayerTest[39934:15a13] drawLayer:inContext: bounds={{300, 300}, {20, 180}} 2012-10-27 15:51:38.792 TiledLayerTest[39934:15a13] end drawLayer:inContext: bounds={{300, 300}, {20, 180}} 2012-10-27 15:51:38.793 TiledLayerTest[39934:1570f] end drawLayer:inContext: bounds={{0, 0}, {300, 300}} 2012-10-27 15:51:38.795 TiledLayerTest[39934:1540f] setNeedsDisplayInRect:{{0, 0}, {300, 300}} 2012-10-27 15:51:38.795 TiledLayerTest[39934:1540f] setNeedsDisplayInRect:{{300, 300}, {20, 180}} 2012-10-27 15:51:38.798 TiledLayerTest[39934:15a13] drawLayer:inContext: bounds={{0, 0}, {300, 300}} 2012-10-27 15:51:38.800 TiledLayerTest[39934:15a13] end drawLayer:inContext: bounds={{0, 0}, {300, 300}} 2012-10-27 15:51:38.802 TiledLayerTest[39934:1630b] setNeedsDisplayInRect:{{0, 0}, {300, 300}} 2012-10-27 15:51:38.806 TiledLayerTest[39934:1570f] drawLayer:inContext: bounds={{0, 300}, {300, 180}} 2012-10-27 15:51:38.808 TiledLayerTest[39934:1630b] setNeedsDisplayInRect:{{0, 300}, {300, 180}} 2012-10-27 15:51:38.809 TiledLayerTest[39934:1570f] end drawLayer:inContext: bounds={{0, 300}, {300, 180}} 2012-10-27 15:51:38.813 TiledLayerTest[39934:15a13] drawLayer:inContext: bounds={{0, 300}, {300, 180}} 2012-10-27 15:51:38.816 TiledLayerTest[39934:1630b] setNeedsDisplayInRect:{{0, 300}, {300, 180}} 2012-10-27 15:51:38.816 TiledLayerTest[39934:15a13] end drawLayer:inContext: bounds={{0, 300}, {300, 180}} 2012-10-27 15:51:43.774 TiledLayerTest[39934:1540f] drawLayer:inContext: bounds={{0, 300}, {300, 180}} 2012-10-27 15:51:43.776 TiledLayerTest[39934:1540f] end drawLayer:inContext: bounds={{0, 300}, {300, 180}} 2012-10-27 15:51:43.776 TiledLayerTest[39934:1630f] drawLayer:inContext: bounds={{0, 0}, {300, 300}} 
+5
ios cocoa-touch drawing
source share
2 answers

I sent my answer to a similar problem: setNeedsDisplayInMapRect does not start a new drawMapRect: call (just a link so as not to duplicate the answer here).

In short: you have to send a call to the setNeedsDisplayInRect method for dispatch_get_main_queue ().

0
source share

You can try using NSRecursiveLock as follows:

 - (void) setNeedsDisplayInRect:(CGRect)rect { [self.lock lock] NSLog(@"setNeedsDisplayInRect:%@", NSStringFromCGRect(rect)); [super setNeedsDisplayInRect:rect]; [self.lock unlock] } - (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)gc { [self.lock lock] CGRect bounds = CGContextGetClipBoundingBox(gc); NSLog(@"drawLayer:inContext: bounds=%@", NSStringFromCGRect(bounds)); // drawing code NSLog(@"end drawLayer:inContext: bounds=%@", NSStringFromCGRect(bounds)); [self.lock unlock] } 

This ensures that setNeedsDisplayInRect will not be called at runtime. However, this can affect performance, because lock can block the main thread, and you cannot draw multiple fragments in parallel.

-one
source share

All Articles