我在我的应用程序中使用了CATiledLayer,因此,该图层的绘制是在后台线程中完成的。也就是说,我的委托的drawLayer:inContext:方法是从后台线程调用的。用于使CATiledLayer部分无效的setNeedsDisplayInRect总是从主线程调用。
因为它们是独立的线程,所以有时会在后台线程处于drawLayer:inContext方法中时调用setNeedsDisplayInRect。我注意到在那种情况下会忽略setNeedsDisplayInRect(不再调用drawLayer:inContext)。
我已经记录了bug to Apple,因为我认为这是不正确的。但我很难弄清楚如何解决这种情况。你有好主意吗?
编辑:
我使用以下代码测试了Stanislaw的答案:
- (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));
}
如上所述,代码确实导致绘图无限重复,但有时在setNeedsDisplayInRect:和相应的drawLayer:inContext:之间有5秒的延迟,其中没有其他任何事情发生。请参阅下面的日志作为示例。注意不规则行为:在第一秒中,一些图块被重绘多次,其他图块只重绘一次。然后暂停5秒,循环重新开始。
这是在IOS6.0的模拟器上测试的(我选择那个版本,因为早期版本有另一个在6.0中修复的错误:它们有时会绘制两次相同的图块。)
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}}
答案 0 :(得分:0)
我已经发布了类似问题的答案:setNeedsDisplayInMapRect doesn't trigger new drawMapRect: call(这里只是一个不重复答案的链接)。
很快:您应该将 setNeedsDisplayInRect 方法的调用发送到dispatch_get_main_queue()。
答案 1 :(得分:-1)
您可以尝试以这种方式使用NSRecursiveLock
:
- (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]
}
这可确保在绘图进行过程中不会调用setNeedsDisplayInRect
。
但是这会影响性能,因为lock
可能会阻塞主线程,并且您无法并行绘制多个切片。